📄 troubleshooting.md

← Vault

Troubleshooting

Performance

Step Zero — Disable FES

The Friendly Error System (FES) adds massive overhead — up to 10x slowdown. Disable it in every production sketch:

`javascript

// BEFORE any p5 code

p5.disableFriendlyErrors = true;

// Or use p5.min.js instead of p5.js — FES is stripped from minified build

`

Step One — pixelDensity(1)

Retina/HiDPI displays default to 2x or 3x density, multiplying pixel count by 4-9x:

`javascript

function setup() {

pixelDensity(1); // force 1:1 — always do this first

createCanvas(1920, 1080);

}

`

Use Math.* in Hot Loops

p5's sin(), cos(), random(), min(), max(), abs() are wrapper functions with overhead. In hot loops (thousands of iterations per frame), use native Math.*:

`javascript

// SLOW — p5 wrappers

for (let p of particles) {

let a = sin(p.angle);

let d = dist(p.x, p.y, mx, my);

}

// FAST — native Math

for (let p of particles) {

let a = Math.sin(p.angle);

let dx = p.x - mx, dy = p.y - my;

let dSq = dx dx + dy dy; // skip sqrt entirely

}

`

Use magSq() instead of mag() for distance comparisons — avoids expensive sqrt().

Diagnosis

Open Chrome DevTools > Performance tab > Record while sketch runs.

Common bottlenecks:

1. FES enabled — 10x overhead on every p5 function call

2. pixelDensity > 1 — 4x pixel count, 4x slower

3. Too many draw calls — thousands of ellipse(), rect() per frame

4. Large canvas + pixel operationsloadPixels()/updatePixels() on 4K canvas

5. Unoptimized particle systems — checking all-vs-all distances (O(n^2))

6. Memory leaks — creating objects every frame without cleanup

7. Shader compilation — calling createShader() in draw() instead of setup()

8. console.log() in draw() — DOM write per frame, destroys performance

9. DOM manipulation in draw() — layout thrashing (400-500x slower than canvas ops)

Solutions

Reduce draw calls:

`javascript

// BAD: 10000 individual circles

for (let p of particles) {

ellipse(p.x, p.y, p.size);

}

// GOOD: single shape with vertices

beginShape(POINTS);

for (let p of particles) {

vertex(p.x, p.y);

}

endShape();

// BEST: direct pixel manipulation

loadPixels();

for (let p of particles) {

let idx = 4 (floor(p.y) width + floor(p.x));

pixels[idx] = p.r;

pixels[idx+1] = p.g;

pixels[idx+2] = p.b;

pixels[idx+3] = 255;

}

updatePixels();

`

Spatial hashing for neighbor queries:

`javascript

class SpatialHash {

constructor(cellSize) {

this.cellSize = cellSize;

this.cells = new Map();

}

clear() { this.cells.clear(); }

_key(x, y) {

return ${floor(x / this.cellSize)},${floor(y / this.cellSize)};

}

insert(obj) {

let key = this._key(obj.pos.x, obj.pos.y);

if (!this.cells.has(key)) this.cells.set(key, []);

this.cells.get(key).push(obj);

}

query(x, y, radius) {

let results = [];

let minCX = floor((x - radius) / this.cellSize);

let maxCX = floor((x + radius) / this.cellSize);

let minCY = floor((y - radius) / this.cellSize);

let maxCY = floor((y + radius) / this.cellSize);

for (let cx = minCX; cx <= maxCX; cx++) {

for (let cy = minCY; cy <= maxCY; cy++) {

let key = ${cx},${cy};

let cell = this.cells.get(key);

if (cell) {

for (let obj of cell) {

if (dist(x, y, obj.pos.x, obj.pos.y) <= radius) {

results.push(obj);

}

}

}

}

}

return results;

}

}

`

Object pooling:

`javascript

class ParticlePool {

constructor(maxSize) {

this.pool = [];

this.active = [];

for (let i = 0; i < maxSize; i++) {

this.pool.push(new Particle(0, 0));

}

}

spawn(x, y) {

let p = this.pool.pop();

if (p) {

p.reset(x, y);

this.active.push(p);

}

}

update() {

for (let i = this.active.length - 1; i >= 0; i--) {

this.active[i].update();

if (this.active[i].isDead()) {

this.pool.push(this.active.splice(i, 1)[0]);

}

}

}

}

`

Throttle heavy operations:

`javascript

// Only update flow field every N frames

if (frameCount % 5 === 0) {

flowField.update(frameCount * 0.001);

}

`

Frame Rate Targets

Per-Pixel Rendering Budgets

Pixel-level operations (loadPixels() loops) are the most expensive common pattern. Budget depends on canvas size and computation per pixel.

ContextTargetAcceptable
-----------------------------
Interactive sketch60fps30fps
Ambient animation30fps20fps
Export/recording30fps renderAny (offline)
Mobile30fps20fps
CanvasPixelsSimple noise (1 call)fBM (4 octave)Domain warp (3-layer fBM)
--------------------------------------------------------------------------------
540x540291K~5ms~20ms~80ms
1080x10801.17M~20ms~80ms~300ms+
1920x10802.07M~35ms~140ms~500ms+
3840x21608.3M~140ms~560msWILL CRASH

Rules of thumb:

Common Causes

`javascript

// 1. Growing arrays

let history = [];

function draw() {

history.push(someData); // grows forever

}

// FIX: cap the array

if (history.length > 1000) history.shift();

// 2. Creating p5 objects in draw()

function draw() {

let v = createVector(0, 0); // allocation every frame

}

// FIX: reuse pre-allocated objects

// 3. Unreleased graphics buffers

let layers = [];

function reset() {

for (let l of layers) l.remove(); // free old buffers

layers = [];

}

// 4. Event listener accumulation

function setup() {

// BAD: adds new listener every time setup runs

window.addEventListener('resize', handler);

}

// FIX: use p5's built-in windowResized()

`

Debugging Tips

Console Logging

`javascript

// Log once (not every frame)

if (frameCount === 1) {

console.log('Canvas:', width, 'x', height);

console.log('Pixel density:', pixelDensity());

console.log('Renderer:', drawingContext.constructor.name);

}

// Log periodically

if (frameCount % 60 === 0) {

console.log('FPS:', frameRate().toFixed(1));

console.log('Particles:', particles.length);

}

`

Visual Debugging

`javascript

// Show frame rate

function draw() {

// ... your sketch ...

if (CONFIG.debug) {

fill(255, 0, 0);

noStroke();

textSize(14);

textAlign(LEFT, TOP);

text('FPS: ' + frameRate().toFixed(1), 10, 10);

text('Particles: ' + particles.length, 10, 28);

text('Frame: ' + frameCount, 10, 46);

}

}

// Toggle debug with 'd' key

function keyPressed() {

if (key === 'd') CONFIG.debug = !CONFIG.debug;

}

`

Isolating Issues

`javascript

// Comment out layers to find the slow one

function draw() {

renderBackground(); // comment out to test

// renderParticles(); // this might be slow

// renderPostEffects(); // or this

}

`