Skip to content
Quinton Ashley edited this page May 13, 2025 · 13 revisions

An addon is software that adds functionality to an existing software system.

Using Addons with q5.js

q5 is compatible with popular p5.js Addon Libraries, such as p5.sound, ml5.js, and p5play.

To use addons, simply load them after q5.js:

<script src="https://q5js.org/q5.js"></script>
<!-- load addons after q5 -->
<script src="https://p5play.org/v3/planck.min.js"></script>
<script src="https://p5play.org/v3/p5play.js"></script>

Developing Addons for q5.js

Consider as a first step, posting your idea for an addon in the #dev channel of the q5 Discord Server. Check if other people would be interested in using it. For your addon to be featured on q5js.org, it needs to have broad appeal and good documentation.

q5's Addon System

Here's a simple example of how to add a lifecycle hook to q5 that runs before the user's draw function.

/* addon.js */

Q5.addHook('predraw', function () {
  const $ = this;
  $.background('pink');
});

/* sketch.js */

function draw() {
  circle(mouseX, mouseY, 50);
}

There are 6 lifecycle hooks:

  • "init"
  • "presetup"
  • "postsetup"
  • "predraw"
  • "postdraw"
  • "remove"

"init" hooks run when a new instance of q5 is created.

Note that inside a hook function this is the instance of q5 and $ is a shortcut for it. You can attach your module's user facing functions, Objects, and classes to it in an "init" hook.

When global mode is enabled, all q5 instance variables are made available in the global scope (window). If you'd like to keep a q5 instance property private, not accessible in the global scope, prefix its name with an underscore.

Supporting both q5 and p5

q5 can load addons made for p5 because it supports p5.registerAddon from v2 and p5.prototype.registerMethod from v1. q5 also adds p5 to the global scope as an alias for Q5.

p5 v1 had a great addon system but p5 v2 replaced it with a more cumbersome API that offers less features (it's still missing an "init" hook as of May 2025). It also requires that lifecycle hooks be defined on a lifecycles object, forcing nearly all your addon code to be written with two indentations minimum. More info: https://dev.to/limzykenneth/designing-an-addon-library-system-for-p5js-20-3d4p

So if you want to make an addon that supports q5 and p5 v2, consider doing it the q5 way.

This example provides code you can copy/paste into your own addon code, which adds the addHook function to p5 and makes Q5 an alias for p5.

/* addon.js */

if (typeof window == 'object' && !window.Q5) {
  window.Q5 = window.p5;
  p5.addLifecycleHook = (name, fn) => {
    p5.registerAddon((p5, proto, lifecycles) => (lifecycles[name] = fn));
  };
}

// your addon code
Q5.addHook('predraw', function () {
  const $ = this;
  $.background('pink');
});

/* sketch.js */

function setup() {
  createCanvas(200, 200);
}

function draw() {
  circle(mouseX, mouseY, 50);
}

This will work with q5 and p5!

Proxy

Note that this feature is not available in p5.

In q5 an input param q is passed to lifecycle hook functions.

q is a JS Proxy between the q5 instance and copies of non-Object properties in the global scope (if global mode is enabled). For example, q.frameCount++ updates the frame count number stored in $.frameCount and window.frameCount. For best performance, your addon should only use q to update the value of non-Objects.

Adding Preload-able Functions

To implement a function, like loadImage, that uses q5 v3's preload system, make it add a Promise to the $._preloadPromises array. Since v3, q5 uses Promise.all to wait until all preload promises are no longer pending before running setup and draw.

It's not recommended, but q5 also supports the legacy p5 v1 way of registering preload methods.

Q5.prototype.registerPreloadMethod("functionName", objectWithFnProperty);

Note that legacy preload methods are responsible for calling $._decrementPreload().

Creating q5 Module Addons

q5-core.js creates an instance of q5 by adding q5 modules, then q5 renderer modules, and then "init" lifecycle hooks.

Q5.modules.yourModule = ($, q) => {
  // your q5 module code
};