p5.js Instance Mode vs Global: A Practical Architecture
Global mode attaches p5 functions to window, causing namespace collisions. Instance mode isolates sketches in a container, reducing interference and improving trust boundaries.
06 May 2026, 15:17 UTC

Problem and Takeaway
Many web projects embed p5.js sketches alongside third‑party widgets, ads, or multiple interactive modules. The default global mode attaches setup(), draw(), and createCanvas() to window, which means any script on the page can read or overwrite the sketch’s frame rate, canvas state, or global functions. This creates a fragile trust boundary: a single overlooked background() call from an ad network can corrupt another sketch’s visuals, and namespace collisions become inevitable when two sketches share a page. The useful takeaway is to reach for instance mode with a dedicated container and a minimal control API when embedding p5.js in production pages.
Requirements
- A modern p5.js build (1.9+ recommended). Instance mode has been available since the library’s early releases and is the recommended pattern for new projects.
- A DOM element serving as the sketch container. The element must have a unique ID or be queryable via a stable selector.
- Optional: TypeScript or ESM project configuration if importing p5 as a module. Instance mode plays well with module bundlers when the sketch is defined in a closure.
- Awareness of the page’s WebGL context limit (typically 16 contexts per page, regardless of instance count).
Smallest Suitable Design for an Embeddable Widget
When the goal is a single, self‑contained sketch that can be dropped into an existing page without interfering with other scripts, the smallest suitable design uses instance mode with a dedicated container element and a configuration object passed to the sketch closure.
// sketch.js
const widgetSketch = {
setup() {
const canvas = createCanvas(300, 200);
canvas.parent("widget-container");
background(220);
},
draw() {
// visual logic here
}
};
// host page
import { p5 } from "p5";
const instance = new p5(widgetSketch, document.getElementById("widget-container"));
Configuration can be passed into the closure:
const widgetSketch = {
params: {}
};
const instance = new p5((s) => {
s.setup = () => {
s.createCanvas(300, 200);
s.background(s.params.bgColor || "#fff");
s.parent("widget-container");
};
s.draw = () => {
// draw using s.params
};
}, document.getElementById("widget-container"));
instance.params = { bgColor: "#e0e0e0" };
Expose a minimal control API to the host page:
function playWidget() {
instance.startPaused = false;
}
function disposeWidget() {
instance.remove();
// optional: replace container with placeholder
document.getElementById("widget-container").innerHTML = "";
}
Trust and Data Boundaries
In global mode, p5 functions live on window. Any script on the page – including third‑party ad tags, chat widgets, or analytics – can call background("red"), frameRate(30), or noLoop() and affect every sketch on the page. This is a broad trust boundary: the sketch author must assume all other scripts on the page are well‑behaved.
Instance mode confines the p5 API to the returned instance. Calls like background() only affect the sketch associated with that instance. If a third‑party script attempts background("red") in a page that uses instance mode exclusively, the call either fails (if the script does not have a reference to the p5 instance) or has no cross‑sketch effect. This reduces the attack surface for namespace pollution and visual interference.
Practical boundary: even in instance mode, code outside the sketch closure can still reach window and execute global p5 functions if they are loaded. The isolation is strongest when the entire page adopts instance mode and no global p5 references are retained.
Operational Checks
To verify that instance mode is working as expected on a given page, run the following checks in the browser DevTools console (or a test environment):
- Global leakage check: Run
window.setup === undefined. In pure instance mode, this should evaluate totrue, confirming that p5 has not attachedsetup,draw, or other globals to the window object. - Independent frame rate check: Open two instance‑mode sketches on the same page, each in its own container. In the console of each sketch’s
draw(), log a unique identifier. Confirm that each sketch’s frame rate operates independently; logging from onedrawshould not appear in the other’s console output. - Third‑party interference test: Load a script that attempts
background("blue"). In instance mode, this should raise aReferenceErrorif the script lacks a p5 instance reference, or silently fail to affect the sketch. In global mode, the sketch’s canvas would turn blue. - remove() cleanup check: Call
instance.remove()on one sketch. In DevToolsEvent Listeners, verify that p5’s event listeners (e.g., resize, mouse) are removed from the canvas and document. Confirm the canvas DOM element is detached and the remaining sketch continues to run without frame drops or errors. - WebGL context count: If the page hosts many instances, open
chrome://gpuor usenavigator.getUserMediadiagnostics to watch for context loss warnings. Hitting 16 contexts on the page is the typical hard limit; adding a 17th instance may cause silent failures or fallback to 2D canvas.
Failure Modes
- Mixed‑mode bugs: If a page loads one sketch in global mode and another in instance mode, the global
draw()from the first sketch will override the second sketch’s animation loop, causing subtle timing and visual bugs. Always audit the page for anysetup()ordraw()definitions that leak towindow. - Forgetting
remove(): Dynamic instances that are created and destroyed without callingremove()leave DOM elements and event listeners attached. Over time, this accumulates and can cause memory pressure or prevent garbage collection of WebGL contexts. - WebGL context exhaustion: The per‑page limit of approximately 16 WebGL contexts is not per‑instance. A page with 20 instance‑mode sketches that each allocate a WebGL canvas will eventually hit this limit, after which new instances fall back to 2D rendering or fail to initialize.
- Legacy library incompatibility: Libraries such as p5.sound or community addons often assume global p5 state. In instance mode, these must be initialized with a reference to the p5 instance (e.g.,
new p5.SoundFile()inside the sketch closure) or patched to accept an instance parameter.
Conditions That Would Change the Design
- No guaranteed unique container ID: If the host page cannot ensure a unique container element ID (e.g., in a CMS with auto‑generated IDs), consider using the
shadow DOMto encapsulate the sketch and its container, or fall back to global mode with explicit namespace cleanup. - Server‑side rendering: When p5.js must run in a Node environment for headless rendering or image generation, use p5.js with the
node‑canvasaddon or thep5‑nodebridge. Instance mode still applies, but the sketch must be structured for a headless canvas rather than a browser DOM. - Bundle size critical: If minimizing JavaScript payload is a priority, evaluate whether tree‑shaking with a modern bundler (Vite, Webpack 5+) retains only the p5 methods in use when importing via
import p5 from "p5". Alternatively, explore lightweight canvas‑sketch utilities that omit p5’s DOM abstraction if only WebGL drawing is needed.
Verification Checklist
- Confirm
window.setup === undefinedafter loading all scripts. - Create two instance‑mode sketches on separate containers; log unique IDs in each
draw()and verify no cross‑talk. - Load a third‑party script attempting
background("red"); verify it fails or has no effect on instance‑mode sketches. - Call
instance.remove()on one sketch; check DevTools Event Listeners and confirm the other sketch continues running. - Inspect WebGL context count via
canvas.getContext("webgl")and watch for loss warnings as instance count grows.
Instance mode is not a silver bullet – it shifts the responsibility of isolation to the developer, requiring deliberate container management, API exposure, and awareness of WebGL limits. However, for production pages that embed sketches alongside other scripts, it is the most robust pattern for maintaining visual independence and preventing namespace collisions.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.