p5.js Production Pipeline
When to use
Use when users request: p5.js sketches, creative coding, generative art, interactive visualizations, canvas animations, browser-based visual art, data viz, shader effects, or any p5.js project.
What's inside
Production pipeline for interactive and generative visual art using p5.js. Creates browser-based sketches, generative art, data visualizations, interactive experiences, 3D scenes, audio-reactive visuals, and motion graphics — exported as HTML, PNG, GIF, MP4, or SVG. Covers: 2D/3D rendering, noise and particle systems, flow fields, shaders (GLSL), pixel manipulation, kinetic typography, WebGL scenes, audio analysis, mouse/keyboard interaction, and headless high-res export.
Creative Standard
This is visual art rendered in the browser. The canvas is the medium; the algorithm is the brush.
Before writing a single line of code, articulate the creative concept. What does this piece communicate? What makes the viewer stop scrolling? What separates this from a code tutorial example? The user's prompt is a starting point — interpret it with creative ambition.
First-render excellence is non-negotiable. The output must be visually striking on first load. If it looks like a p5.js tutorial exercise, a default configuration, or "AI-generated creative coding," it is wrong. Rethink before shipping.
Go beyond the reference vocabulary. The noise functions, particle systems, color palettes, and shader effects in the references are a starting vocabulary. For every project, combine, layer, and invent. The catalog is a palette of paints — you write the painting.
Be proactively creative. If the user asks for "a particle system," deliver a particle system with emergent flocking behavior, trailing ghost echoes, palette-shifted depth fog, and a background noise field that breathes. Include at least one visual detail the user didn't ask for but will appreciate.
Dense, layered, considered. Every frame should reward viewing. Never flat white backgrounds. Always compositional hierarchy. Always intentional color. Always micro-detail that only appears on close inspection.
Cohesive aesthetic over feature count. All elements must serve a unified visual language — shared color temperature, consistent stroke weight vocabulary, harmonious motion speeds. A sketch with ten unrelated effects is worse than one with three that belong together.
Modes
| Mode |
Input |
Output |
Reference |
| Generative art |
Seed / parameters |
Procedural visual composition (still or animated) |
references/visual-effects.md |
| Data visualization |
Dataset / API |
Interactive charts, graphs, custom data displays |
references/interaction.md |
| Interactive experience |
None (user drives) |
Mouse/keyboard/touch-driven sketch |
references/interaction.md |
| Animation / motion graphics |
Timeline / storyboard |
Timed sequences, kinetic typography, transitions |
references/animation.md |
| 3D scene |
Concept description |
WebGL geometry, lighting, camera, materials |
references/webgl-and-3d.md |
| Image processing |
Image file(s) |
Pixel manipulation, filters, mosaic, pointillism |
references/visual-effects.md § Pixel Manipulation |
| Audio-reactive |
Audio file / mic |
Sound-driven generative visuals |
references/interaction.md § Audio Input |
Stack
Single self-contained HTML file per project. No build step required.
| Layer |
Tool |
Purpose |
| Core |
p5.js 1.11.3 (CDN) |
Canvas rendering, math, transforms, event handling |
| 3D |
p5.js WebGL mode |
3D geometry, camera, lighting, GLSL shaders |
| Audio |
p5.sound.js (CDN) |
FFT analysis, amplitude, mic input, oscillators |
| Export |
Built-in saveCanvas() / saveGif() / saveFrames() |
PNG, GIF, frame sequence output |
| Capture |
CCapture.js (optional) |
Deterministic framerate video capture (WebM, GIF) |
| Headless |
Puppeteer + Node.js (optional) |
Automated high-res rendering, MP4 via ffmpeg |
| SVG |
p5.js-svg 1.6.0 (optional) |
Vector output for print — requires p5.js 1.x |
| Natural media |
p5.brush (optional) |
Watercolor, charcoal, pen — requires p5.js 2.x + WEBGL |
| Texture |
p5.grain (optional) |
Film grain, texture overlays |
| Fonts |
Google Fonts / loadFont() |
Custom typography via OTF/TTF/WOFF2 |
Version Note
p5.js 1.x (1.11.3) is the default — stable, well-documented, broadest library compatibility. Use this unless a project requires 2.x features.
p5.js 2.x (2.2+) adds: async setup() replacing preload(), OKLCH/OKLAB color modes, splineVertex(), shader .modify() API, variable fonts, textToContours(), pointer events. Required for p5.brush. See references/core-api.md § p5.js 2.0.
Pipeline
Every project follows the same 6-stage path:
CONCEPT → DESIGN → CODE → PREVIEW → EXPORT → VERIFY
- CONCEPT — Articulate the creative vision: mood, color world, motion vocabulary, what makes this unique
- DESIGN — Choose mode, canvas size, interaction model, color system, export format. Map concept to technical decisions
- CODE — Write single HTML file with inline p5.js. Structure: globals →
preload() → setup() → draw() → helpers → classes → event handlers
- PREVIEW — Open in browser, verify visual quality. Test at target resolution. Check performance
- EXPORT — Capture output:
saveCanvas() for PNG, saveGif() for GIF, saveFrames() + ffmpeg for MP4, Puppeteer for headless batch
- VERIFY — Does the output match the concept? Is it visually striking at the intended display size? Would you frame it?
Creative Direction
Aesthetic Dimensions
| Dimension |
Options |
Reference |
| Color system |
HSB/HSL, RGB, named palettes, procedural harmony, gradient interpolation |
references/color-systems.md |
| Noise vocabulary |
Perlin noise, simplex, fractal (octaved), domain warping, curl noise |
references/visual-effects.md § Noise |
| Particle systems |
Physics-based, flocking, trail-drawing, attractor-driven, flow-field following |
references/visual-effects.md § Particles |
| Shape language |
Geometric primitives, custom vertices, bezier curves, SVG paths |
references/shapes-and-geometry.md |
| Motion style |
Eased, spring-based, noise-driven, physics sim, lerped, stepped |
references/animation.md |
| Typography |
System fonts, loaded OTF, textToPoints() particle text, kinetic |
references/typography.md |
| Shader effects |
GLSL fragment/vertex, filter shaders, post-processing, feedback loops |
references/webgl-and-3d.md § Shaders |
| Composition |
Grid, radial, golden ratio, rule of thirds, organic scatter, tiled |
references/core-api.md § Composition |
| Interaction model |
Mouse follow, click spawn, drag, keyboard state, scroll-driven, mic input |
references/interaction.md |
| Blend modes |
BLEND, ADD, MULTIPLY, SCREEN, DIFFERENCE, EXCLUSION, OVERLAY |
references/color-systems.md § Blend Modes |
| Layering |
createGraphics() offscreen buffers, alpha compositing, masking |
references/core-api.md § Offscreen Buffers |
| Texture |
Perlin surface, stippling, hatching, halftone, pixel sorting |
references/visual-effects.md § Texture Generation |
Per-Project Variation Rules
Never use default configurations. For every project:
- Custom color palette — never raw
fill(255, 0, 0). Always a designed palette with 3-7 colors
- Custom stroke weight vocabulary — thin accents (0.5), medium structure (1-2), bold emphasis (3-5)
- Background treatment — never plain
background(0) or background(255). Always textured, gradient, or layered
- Motion variety — different speeds for different elements. Primary at 1x, secondary at 0.3x, ambient at 0.1x
- At least one invented element — a custom particle behavior, a novel noise application, a unique interaction response
Project-Specific Invention
For every project, invent at least one of:
- A custom color palette matching the mood (not a preset)
- A novel noise field combination (e.g., curl noise + domain warp + feedback)
- A unique particle behavior (custom forces, custom trails, custom spawning)
- An interaction mechanic the user didn't request but that elevates the piece
- A compositional technique that creates visual hierarchy
Parameter Design Philosophy
Parameters should emerge from the algorithm, not from a generic menu. Ask: "What properties of this system should be tunable?"
Good parameters expose the algorithm's character:
- Quantities — how many particles, branches, cells (controls density)
- Scales — noise frequency, element size, spacing (controls texture)
- Rates — speed, growth rate, decay (controls energy)
- Thresholds — when does behavior change? (controls drama)
- Ratios — proportions, balance between forces (controls harmony)
Bad parameters are generic controls unrelated to the algorithm:
- "color1", "color2", "size" — meaningless without context
- Toggle switches for unrelated effects
- Parameters that only change cosmetics, not behavior
Every parameter should change how the algorithm thinks, not just how it looks. A "turbulence" parameter that changes noise octaves is good. A "particle size" slider that only changes ellipse() radius is shallow.
Workflow
Step 1: Creative Vision
Before any code, articulate:
- Mood / atmosphere: What should the viewer feel? Contemplative? Energized? Unsettled? Playful?
- Visual story: What happens over time (or on interaction)? Build? Decay? Transform? Oscillate?
- Color world: Warm/cool? Monochrome? Complementary? What's the dominant hue? The accent?
- Shape language: Organic curves? Sharp geometry? Dots? Lines? Mixed?
- Motion vocabulary: Slow drift? Explosive burst? Breathing pulse? Mechanical precision?
- What makes THIS different: What is the one thing that makes this sketch unique?
Map the user's prompt to aesthetic choices. "Relaxing generative background" demands different everything from "glitch data visualization."
Step 2: Technical Design
- Mode — which of the 7 modes from the table above
- Canvas size — landscape 1920x1080, portrait 1080x1920, square 1080x1080, or responsive
windowWidth/windowHeight
- Renderer —
P2D (default) or WEBGL (for 3D, shaders, advanced blend modes)
- Frame rate — 60fps (interactive), 30fps (ambient animation), or
noLoop() (static generative)
- Export target — browser display, PNG still, GIF loop, MP4 video, SVG vector
- Interaction model — passive (no input), mouse-driven, keyboard-driven, audio-reactive, scroll-driven
- Viewer UI — for interactive generative art, start from
templates/viewer.html which provides seed navigation, parameter sliders, and download. For simple sketches or video export, use bare HTML
Step 3: Code the Sketch
For interactive generative art (seed exploration, parameter tuning): start from templates/viewer.html. Read the template first, keep the fixed sections (seed nav, actions), replace the algorithm and parameter controls. This gives the user seed prev/next/random/jump, parameter sliders with live update, and PNG download — all wired up.
For animations, video export, or simple sketches: use bare HTML:
Single HTML file. Structure:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Project Name</title>
<script>p5.disableFriendlyErrors = true;</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.3/p5.min.js"></script>
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.3/addons/p5.sound.min.js"></script> -->
<!-- <script src="https://unpkg.com/p5.js-svg@1.6.0"></script> --> <!-- SVG export -->
<!-- <script src="https://cdn.jsdelivr.net/npm/ccapture.js-npmfixed/build/CCapture.all.min.js"></script> --> <!-- video capture -->
<style>
html, body { margin: 0; padding: 0; overflow: hidden; }
canvas { display: block; }
</style>
</head>
<body>
<script>
// === Configuration ===
const CONFIG = {
seed: 42,
// ... project-specific params
};
// === Color Palette ===
const PALETTE = {
bg: '#0a0a0f',
primary: '#e8d5b7',
// ...
};
// === Global State ===
let particles = [];
// === Preload (fonts, images, data) ===
function preload() {
// font = loadFont('...');
}
// === Setup ===
function setup() {
createCanvas(1920, 1080);
randomSeed(CONFIG.seed);
noiseSeed(CONFIG.seed);
colorMode(HSB, 360, 100, 100, 100);
// Initialize state...
}
// === Draw Loop ===
function draw() {
// Render frame...
}
// === Helper Functions ===
// ...
// === Classes ===
class Particle {
// ...
}
// === Event Handlers ===
function mousePressed() { /* ... */ }
function keyPressed() { /* ... */ }
function windowResized() { resizeCanvas(windowWidth, windowHeight); }
</script>
</body>
</html>
Key implementation patterns:
- Seeded randomness: Always
randomSeed() + noiseSeed() for reproducibility
- Color mode: Use
colorMode(HSB, 360, 100, 100, 100) for intuitive color control
- State separation: CONFIG for parameters, PALETTE for colors, globals for mutable state
- Class-based entities: Particles, agents, shapes as classes with
update() + display() methods
- Offscreen buffers:
createGraphics() for layered composition, trails, masks
Step 4: Preview & Iterate
- Open HTML file directly in browser — no server needed for basic sketches
- For
loadImage()/loadFont() from local files: use scripts/serve.sh or python3 -m http.server
- Chrome DevTools Performance tab to verify 60fps
- Test at target export resolution, not just the window size
- Adjust parameters until the visual matches the concept from Step 1
Step 5: Export
| Format |
Method |
Command |
| PNG |
saveCanvas('output', 'png') in keyPressed() |
Press 's' to save |
| High-res PNG |
Puppeteer headless capture |
node scripts/export-frames.js sketch.html --width 3840 --height 2160 --frames 1 |
| GIF |
saveGif('output', 5) — captures N seconds |
Press 'g' to save |
| Frame sequence |
saveFrames('frame', 'png', 10, 30) — 10s at 30fps |
Then ffmpeg -i frame-%04d.png -c:v libx264 output.mp4 |
| MP4 |
Puppeteer frame capture + ffmpeg |
bash scripts/render.sh sketch.html output.mp4 --duration 30 --fps 30 |
| SVG |
createCanvas(w, h, SVG) with p5.js-svg |
save('output.svg') |
Step 6: Quality Verification
- Does it match the vision? Compare output to the creative concept. If it looks generic, go back to Step 1
- Resolution check: Is it sharp at the target display size? No aliasing artifacts?
- Performance check: Does it hold 60fps in browser? (30fps minimum for animations)
- Color check: Do the colors work together? Test on both light and dark monitors
- Edge cases: What happens at canvas edges? On resize? After running for 10 minutes?
Critical Implementation Notes
Performance — Disable FES First
The Friendly Error System (FES) adds up to 10x overhead. Disable it in every production sketch:
p5.disableFriendlyErrors = true; // BEFORE setup()
function setup() {
pixelDensity(1); // prevent 2x-4x overdraw on retina
createCanvas(1920, 1080);
}
In hot loops (particles, pixel ops), use Math.* instead of p5 wrappers — measurably faster:
// In draw() or update() hot paths:
let a = Math.sin(t); // not sin(t)
let r = Math.sqrt(dx*dx+dy*dy); // not dist() — or better: skip sqrt, compare magSq
let v = Math.random(); // not random() — when seed not needed
let m = Math.min(a, b); // not min(a, b)
Never console.log() inside draw(). Never manipulate DOM in draw(). See references/troubleshooting.md § Performance.
Seeded Randomness — Always
Every generative sketch must be reproducible. Same seed, same output.
function setup() {
randomSeed(CONFIG.seed);
noiseSeed(CONFIG.seed);
// All random() and noise() calls now deterministic
}
Never use Math.random() for generative content — only for performance-critical non-visual code. Always random() for visual elements. If you need a random seed: CONFIG.seed = floor(random(99999)).
Generative Art Platform Support (fxhash / Art Blocks)
For generative art platforms, replace p5's PRNG with the platform's deterministic random:
// fxhash convention
const SEED = $fx.hash; // unique per mint
const rng = $fx.rand; // deterministic PRNG
$fx.features({ palette: 'warm', complexity: 'high' });
// In setup():
randomSeed(SEED); // for p5's noise()
noiseSeed(SEED);
// Replace random() with rng() for platform determinism
let x = rng() * width; // instead of random(width)
See references/export-pipeline.md § Platform Export.
Color Mode — Use HSB
HSB (Hue, Saturation, Brightness) is dramatically easier to work with than RGB for generative art:
colorMode(HSB, 360, 100, 100, 100);
// Now: fill(hue, sat, bri, alpha)
// Rotate hue: fill((baseHue + offset) % 360, 80, 90)
// Desaturate: fill(hue, sat * 0.3, bri)
// Darken: fill(hue, sat, bri * 0.5)
Never hardcode raw RGB values. Define a palette object, derive variations procedurally. See references/color-systems.md.
Noise — Multi-Octave, Not Raw
Raw noise(x, y) looks like smooth blobs. Layer octaves for natural texture:
function fbm(x, y, octaves = 4) {
let val = 0, amp = 1, freq = 1, sum = 0;
for (let i = 0; i < octaves; i++) {
val += noise(x * freq, y * freq) * amp;
sum += amp;
amp *= 0.5;
freq *= 2;
}
return val / sum;
}
For flowing organic forms, use domain warping: feed noise output back as noise input coordinates. See references/visual-effects.md.
createGraphics() for Layers — Not Optional
Flat single-pass rendering looks flat. Use offscreen buffers for composition:
let bgLayer, fgLayer, trailLayer;
function setup() {
createCanvas(1920, 1080);
bgLayer = createGraphics(width, height);
fgLayer = createGraphics(width, height);
trailLayer = createGraphics(width, height);
}
function draw() {
renderBackground(bgLayer);
renderTrails(trailLayer); // persistent, fading
renderForeground(fgLayer); // cleared each frame
image(bgLayer, 0, 0);
image(trailLayer, 0, 0);
image(fgLayer, 0, 0);
}
Performance — Vectorize Where Possible
p5.js draw calls are expensive. For thousands of particles:
// SLOW: individual shapes
for (let p of particles) {
ellipse(p.x, p.y, p.size);
}
// FAST: single shape with beginShape()
beginShape(POINTS);
for (let p of particles) {
vertex(p.x, p.y);
}
endShape();
// FASTEST: pixel buffer for massive counts
loadPixels();
for (let p of particles) {
let idx = 4 * (floor(p.y) * width + floor(p.x));
pixels[idx] = r; pixels[idx+1] = g; pixels[idx+2] = b; pixels[idx+3] = 255;
}
updatePixels();
See references/troubleshooting.md § Performance.
Instance Mode for Multiple Sketches
Global mode pollutes window. For production, use instance mode:
const sketch = (p) => {
p.setup = function() {
p.createCanvas(800, 800);
};
p.draw = function() {
p.background(0);
p.ellipse(p.mouseX, p.mouseY, 50);
};
};
new p5(sketch, 'canvas-container');
Required when embedding multiple sketches on one page or integrating with frameworks.
WebGL Mode Gotchas
createCanvas(w, h, WEBGL) — origin is center, not top-left
- Y-axis is inverted (positive Y goes up in WEBGL, down in P2D)
translate(-width/2, -height/2) to get P2D-like coordinates
push()/pop() around every transform — matrix stack overflows silently
texture() before rect()/plane() — not after
- Custom shaders:
createShader(vert, frag) — test on multiple browsers
Export — Key Bindings Convention
Every sketch should include these in keyPressed():
function keyPressed() {
if (key === 's' || key === 'S') saveCanvas('output', 'png');
if (key === 'g' || key === 'G') saveGif('output', 5);
if (key === 'r' || key === 'R') { randomSeed(millis()); noiseSeed(millis()); }
if (key === ' ') CONFIG.paused = !CONFIG.paused;
}
Headless Video Export — Use noLoop()
For headless rendering via Puppeteer, the sketch must use noLoop() in setup. Without it, p5's draw loop runs freely while screenshots are slow — the sketch races ahead and you get skipped/duplicate frames.
function setup() {
createCanvas(1920, 1080);
pixelDensity(1);
noLoop(); // capture script controls frame advance
window._p5Ready = true; // signal readiness to capture script
}
The bundled scripts/export-frames.js detects _p5Ready and calls redraw() once per capture for exact 1:1 frame correspondence. See references/export-pipeline.md § Deterministic Capture.
For multi-scene videos, use the per-clip architecture: one HTML per scene, render independently, stitch with ffmpeg -f concat. See references/export-pipeline.md § Per-Clip Architecture.
Agent Workflow
When building p5.js sketches:
- Write the HTML file — single self-contained file, all code inline
- Open in browser —
open sketch.html (macOS) or xdg-open sketch.html (Linux)
- Local assets (fonts, images) require a server:
python3 -m http.server 8080 in the project directory, then open http://localhost:8080/sketch.html
- Export PNG/GIF — add
keyPressed() shortcuts as shown above, tell the user which key to press
- Headless export —
node scripts/export-frames.js sketch.html --frames 300 for automated frame capture (sketch must use noLoop() + _p5Ready)
- MP4 rendering —
bash scripts/render.sh sketch.html output.mp4 --duration 30
- Iterative refinement — edit the HTML file, user refreshes browser to see changes
- Load references on demand — use
skill_view(name="p5js", file_path="references/...") to load specific reference files as needed during implementation
Performance Targets
| Metric |
Target |
| Frame rate (interactive) |
60fps sustained |
| Frame rate (animated export) |
30fps minimum |
| Particle count (P2D shapes) |
5,000-10,000 at 60fps |
| Particle count (pixel buffer) |
50,000-100,000 at 60fps |
| Canvas resolution |
Up to 3840x2160 (export), 1920x1080 (interactive) |
| File size (HTML) |
< 100KB (excluding CDN libraries) |
| Load time |
< 2s to first frame |
References
| File |
Contents |
references/core-api.md |
Canvas setup, coordinate system, draw loop, push()/pop(), offscreen buffers, composition patterns, pixelDensity(), responsive design |
references/shapes-and-geometry.md |
2D primitives, beginShape()/endShape(), Bezier/Catmull-Rom curves, vertex() systems, custom shapes, p5.Vector, signed distance fields, SVG path conversion |
references/visual-effects.md |
Noise (Perlin, fractal, domain warp, curl), flow fields, particle systems (physics, flocking, trails), pixel manipulation, texture generation (stipple, hatch, halftone), feedback loops, reaction-diffusion |
references/animation.md |
Frame-based animation, easing functions, lerp()/map(), spring physics, state machines, timeline sequencing, millis()-based timing, transition patterns |
references/typography.md |
text(), loadFont(), textToPoints(), kinetic typography, text masks, font metrics, responsive text sizing |
references/color-systems.md |
colorMode(), HSB/HSL/RGB, lerpColor(), paletteLerp(), procedural palettes, color harmony, blendMode(), gradient rendering, curated palette library |
references/webgl-and-3d.md |
WEBGL renderer, 3D primitives, camera, lighting, materials, custom geometry, GLSL shaders (createShader(), createFilterShader()), framebuffers, post-processing |
references/interaction.md |
Mouse events, keyboard state, touch input, DOM elements, createSlider()/createButton(), audio input (p5.sound FFT/amplitude), scroll-driven animation, responsive events |
references/export-pipeline.md |
saveCanvas(), saveGif(), saveFrames(), deterministic headless capture, ffmpeg frame-to-video, CCapture.js, SVG export, per-clip architecture, platform export (fxhash), video gotchas |
references/troubleshooting.md |
Performance profiling, per-pixel budgets, common mistakes, browser compatibility, WebGL debugging, font loading issues, pixel density traps, memory leaks, CORS |
templates/viewer.html |
Interactive viewer template: seed navigation (prev/next/random/jump), parameter sliders, download PNG, responsive canvas. Start from this for explorable generative art |
Creative Divergence (use only when user requests experimental/creative/unique output)
If the user asks for creative, experimental, surprising, or unconventional output, select the strategy that best fits and reason through its steps BEFORE generating code.
- Conceptual Blending — when the user names two things to combine or wants hybrid aesthetics
- SCAMPER — when the user wants a twist on a known generative art pattern
- Distance Association — when the user gives a single concept and wants exploration ("make something about time")
Conceptual Blending
- Name two distinct visual systems (e.g., particle physics + handwriting)
- Map correspondences (particles = ink drops, forces = pen pressure, fields = letterforms)
- Blend selectively — keep mappings that produce interesting emergent visuals
- Code the blend as a unified system, not two systems side-by-side
SCAMPER Transformation
Take a known generative pattern (flow field, particle system, L-system, cellular automata) and systematically transform it:
- Substitute: replace circles with text characters, lines with gradients
- Combine: merge two patterns (flow field + voronoi)
- Adapt: apply a 2D pattern to a 3D projection
- Modify: exaggerate scale, warp the coordinate space
- Purpose: use a physics sim for typography, a sorting algorithm for color
- Eliminate: remove the grid, remove color, remove symmetry
- Reverse: run the simulation backward, invert the parameter space
Distance Association
- Anchor on the user's concept (e.g., "loneliness")
- Generate associations at three distances:
- Close (obvious): empty room, single figure, silence
- Medium (interesting): one fish in a school swimming the wrong way, a phone with no notifications, the gap between subway cars
- Far (abstract): prime numbers, asymptotic curves, the color of 3am
- Develop the medium-distance associations — they're specific enough to visualize but unexpected enough to be interesting
Source: NousResearch/hermes-agent → skills/creative/p5js/SKILL.md
1---2name: p5js3description: p5.js sketches: gen art, shaders, interactive, 3D.4---567# p5.js Production Pipeline89## When to use1011Use when users request: p5.js sketches, creative coding, generative art, interactive visualizations, canvas animations, browser-based visual art, data viz, shader effects, or any p5.js project.1213## What's inside1415Production pipeline for interactive and generative visual art using p5.js. Creates browser-based sketches, generative art, data visualizations, interactive experiences, 3D scenes, audio-reactive visuals, and motion graphics — exported as HTML, PNG, GIF, MP4, or SVG. Covers: 2D/3D rendering, noise and particle systems, flow fields, shaders (GLSL), pixel manipulation, kinetic typography, WebGL scenes, audio analysis, mouse/keyboard interaction, and headless high-res export.1617## Creative Standard1819This is visual art rendered in the browser. The canvas is the medium; the algorithm is the brush.2021**Before writing a single line of code**, articulate the creative concept. What does this piece communicate? What makes the viewer stop scrolling? What separates this from a code tutorial example? The user's prompt is a starting point — interpret it with creative ambition.2223**First-render excellence is non-negotiable.** The output must be visually striking on first load. If it looks like a p5.js tutorial exercise, a default configuration, or "AI-generated creative coding," it is wrong. Rethink before shipping.2425**Go beyond the reference vocabulary.** The noise functions, particle systems, color palettes, and shader effects in the references are a starting vocabulary. For every project, combine, layer, and invent. The catalog is a palette of paints — you write the painting.2627**Be proactively creative.** If the user asks for "a particle system," deliver a particle system with emergent flocking behavior, trailing ghost echoes, palette-shifted depth fog, and a background noise field that breathes. Include at least one visual detail the user didn't ask for but will appreciate.2829**Dense, layered, considered.** Every frame should reward viewing. Never flat white backgrounds. Always compositional hierarchy. Always intentional color. Always micro-detail that only appears on close inspection.3031**Cohesive aesthetic over feature count.** All elements must serve a unified visual language — shared color temperature, consistent stroke weight vocabulary, harmonious motion speeds. A sketch with ten unrelated effects is worse than one with three that belong together.3233## Modes3435| Mode | Input | Output | Reference |36|------|-------|--------|-----------|37| **Generative art** | Seed / parameters | Procedural visual composition (still or animated) | `references/visual-effects.md` |38| **Data visualization** | Dataset / API | Interactive charts, graphs, custom data displays | `references/interaction.md` |39| **Interactive experience** | None (user drives) | Mouse/keyboard/touch-driven sketch | `references/interaction.md` |40| **Animation / motion graphics** | Timeline / storyboard | Timed sequences, kinetic typography, transitions | `references/animation.md` |41| **3D scene** | Concept description | WebGL geometry, lighting, camera, materials | `references/webgl-and-3d.md` |42| **Image processing** | Image file(s) | Pixel manipulation, filters, mosaic, pointillism | `references/visual-effects.md` § Pixel Manipulation |43| **Audio-reactive** | Audio file / mic | Sound-driven generative visuals | `references/interaction.md` § Audio Input |4445## Stack4647Single self-contained HTML file per project. No build step required.4849| Layer | Tool | Purpose |50|-------|------|---------|51| Core | p5.js 1.11.3 (CDN) | Canvas rendering, math, transforms, event handling |52| 3D | p5.js WebGL mode | 3D geometry, camera, lighting, GLSL shaders |53| Audio | p5.sound.js (CDN) | FFT analysis, amplitude, mic input, oscillators |54| Export | Built-in `saveCanvas()` / `saveGif()` / `saveFrames()` | PNG, GIF, frame sequence output |55| Capture | CCapture.js (optional) | Deterministic framerate video capture (WebM, GIF) |56| Headless | Puppeteer + Node.js (optional) | Automated high-res rendering, MP4 via ffmpeg |57| SVG | p5.js-svg 1.6.0 (optional) | Vector output for print — requires p5.js 1.x |58| Natural media | p5.brush (optional) | Watercolor, charcoal, pen — requires p5.js 2.x + WEBGL |59| Texture | p5.grain (optional) | Film grain, texture overlays |60| Fonts | Google Fonts / `loadFont()` | Custom typography via OTF/TTF/WOFF2 |6162### Version Note6364**p5.js 1.x** (1.11.3) is the default — stable, well-documented, broadest library compatibility. Use this unless a project requires 2.x features.6566**p5.js 2.x** (2.2+) adds: `async setup()` replacing `preload()`, OKLCH/OKLAB color modes, `splineVertex()`, shader `.modify()` API, variable fonts, `textToContours()`, pointer events. Required for p5.brush. See `references/core-api.md` § p5.js 2.0.6768## Pipeline6970Every project follows the same 6-stage path:7172```73CONCEPT → DESIGN → CODE → PREVIEW → EXPORT → VERIFY74```75761. **CONCEPT** — Articulate the creative vision: mood, color world, motion vocabulary, what makes this unique772. **DESIGN** — Choose mode, canvas size, interaction model, color system, export format. Map concept to technical decisions783. **CODE** — Write single HTML file with inline p5.js. Structure: globals → `preload()` → `setup()` → `draw()` → helpers → classes → event handlers794. **PREVIEW** — Open in browser, verify visual quality. Test at target resolution. Check performance805. **EXPORT** — Capture output: `saveCanvas()` for PNG, `saveGif()` for GIF, `saveFrames()` + ffmpeg for MP4, Puppeteer for headless batch816. **VERIFY** — Does the output match the concept? Is it visually striking at the intended display size? Would you frame it?8283## Creative Direction8485### Aesthetic Dimensions8687| Dimension | Options | Reference |88|-----------|---------|-----------|89| **Color system** | HSB/HSL, RGB, named palettes, procedural harmony, gradient interpolation | `references/color-systems.md` |90| **Noise vocabulary** | Perlin noise, simplex, fractal (octaved), domain warping, curl noise | `references/visual-effects.md` § Noise |91| **Particle systems** | Physics-based, flocking, trail-drawing, attractor-driven, flow-field following | `references/visual-effects.md` § Particles |92| **Shape language** | Geometric primitives, custom vertices, bezier curves, SVG paths | `references/shapes-and-geometry.md` |93| **Motion style** | Eased, spring-based, noise-driven, physics sim, lerped, stepped | `references/animation.md` |94| **Typography** | System fonts, loaded OTF, `textToPoints()` particle text, kinetic | `references/typography.md` |95| **Shader effects** | GLSL fragment/vertex, filter shaders, post-processing, feedback loops | `references/webgl-and-3d.md` § Shaders |96| **Composition** | Grid, radial, golden ratio, rule of thirds, organic scatter, tiled | `references/core-api.md` § Composition |97| **Interaction model** | Mouse follow, click spawn, drag, keyboard state, scroll-driven, mic input | `references/interaction.md` |98| **Blend modes** | `BLEND`, `ADD`, `MULTIPLY`, `SCREEN`, `DIFFERENCE`, `EXCLUSION`, `OVERLAY` | `references/color-systems.md` § Blend Modes |99| **Layering** | `createGraphics()` offscreen buffers, alpha compositing, masking | `references/core-api.md` § Offscreen Buffers |100| **Texture** | Perlin surface, stippling, hatching, halftone, pixel sorting | `references/visual-effects.md` § Texture Generation |101102### Per-Project Variation Rules103104Never use default configurations. For every project:105- **Custom color palette** — never raw `fill(255, 0, 0)`. Always a designed palette with 3-7 colors106- **Custom stroke weight vocabulary** — thin accents (0.5), medium structure (1-2), bold emphasis (3-5)107- **Background treatment** — never plain `background(0)` or `background(255)`. Always textured, gradient, or layered108- **Motion variety** — different speeds for different elements. Primary at 1x, secondary at 0.3x, ambient at 0.1x109- **At least one invented element** — a custom particle behavior, a novel noise application, a unique interaction response110111### Project-Specific Invention112113For every project, invent at least one of:114- A custom color palette matching the mood (not a preset)115- A novel noise field combination (e.g., curl noise + domain warp + feedback)116- A unique particle behavior (custom forces, custom trails, custom spawning)117- An interaction mechanic the user didn't request but that elevates the piece118- A compositional technique that creates visual hierarchy119120### Parameter Design Philosophy121122Parameters should emerge from the algorithm, not from a generic menu. Ask: "What properties of *this* system should be tunable?"123124**Good parameters** expose the algorithm's character:125- **Quantities** — how many particles, branches, cells (controls density)126- **Scales** — noise frequency, element size, spacing (controls texture)127- **Rates** — speed, growth rate, decay (controls energy)128- **Thresholds** — when does behavior change? (controls drama)129- **Ratios** — proportions, balance between forces (controls harmony)130131**Bad parameters** are generic controls unrelated to the algorithm:132- "color1", "color2", "size" — meaningless without context133- Toggle switches for unrelated effects134- Parameters that only change cosmetics, not behavior135136Every parameter should change how the algorithm *thinks*, not just how it *looks*. A "turbulence" parameter that changes noise octaves is good. A "particle size" slider that only changes `ellipse()` radius is shallow.137138## Workflow139140### Step 1: Creative Vision141142Before any code, articulate:143144- **Mood / atmosphere**: What should the viewer feel? Contemplative? Energized? Unsettled? Playful?145- **Visual story**: What happens over time (or on interaction)? Build? Decay? Transform? Oscillate?146- **Color world**: Warm/cool? Monochrome? Complementary? What's the dominant hue? The accent?147- **Shape language**: Organic curves? Sharp geometry? Dots? Lines? Mixed?148- **Motion vocabulary**: Slow drift? Explosive burst? Breathing pulse? Mechanical precision?149- **What makes THIS different**: What is the one thing that makes this sketch unique?150151Map the user's prompt to aesthetic choices. "Relaxing generative background" demands different everything from "glitch data visualization."152153### Step 2: Technical Design154155- **Mode** — which of the 7 modes from the table above156- **Canvas size** — landscape 1920x1080, portrait 1080x1920, square 1080x1080, or responsive `windowWidth/windowHeight`157- **Renderer** — `P2D` (default) or `WEBGL` (for 3D, shaders, advanced blend modes)158- **Frame rate** — 60fps (interactive), 30fps (ambient animation), or `noLoop()` (static generative)159- **Export target** — browser display, PNG still, GIF loop, MP4 video, SVG vector160- **Interaction model** — passive (no input), mouse-driven, keyboard-driven, audio-reactive, scroll-driven161- **Viewer UI** — for interactive generative art, start from `templates/viewer.html` which provides seed navigation, parameter sliders, and download. For simple sketches or video export, use bare HTML162163### Step 3: Code the Sketch164165For **interactive generative art** (seed exploration, parameter tuning): start from `templates/viewer.html`. Read the template first, keep the fixed sections (seed nav, actions), replace the algorithm and parameter controls. This gives the user seed prev/next/random/jump, parameter sliders with live update, and PNG download — all wired up.166167For **animations, video export, or simple sketches**: use bare HTML:168169Single HTML file. Structure:170171```html172<!DOCTYPE html>173<html lang="en">174<head>175 <meta charset="UTF-8">176 <meta name="viewport" content="width=device-width, initial-scale=1.0">177 <title>Project Name</title>178 <script>p5.disableFriendlyErrors = true;</script>179 <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.3/p5.min.js"></script>180 <!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.3/addons/p5.sound.min.js"></script> -->181 <!-- <script src="https://unpkg.com/p5.js-svg@1.6.0"></script> --> <!-- SVG export -->182 <!-- <script src="https://cdn.jsdelivr.net/npm/ccapture.js-npmfixed/build/CCapture.all.min.js"></script> --> <!-- video capture -->183 <style>184 html, body { margin: 0; padding: 0; overflow: hidden; }185 canvas { display: block; }186 </style>187</head>188<body>189<script>190// === Configuration ===191const CONFIG = {192 seed: 42,193 // ... project-specific params194};195196// === Color Palette ===197const PALETTE = {198 bg: '#0a0a0f',199 primary: '#e8d5b7',200 // ...201};202203// === Global State ===204let particles = [];205206// === Preload (fonts, images, data) ===207function preload() {208 // font = loadFont('...');209}210211// === Setup ===212function setup() {213 createCanvas(1920, 1080);214 randomSeed(CONFIG.seed);215 noiseSeed(CONFIG.seed);216 colorMode(HSB, 360, 100, 100, 100);217 // Initialize state...218}219220// === Draw Loop ===221function draw() {222 // Render frame...223}224225// === Helper Functions ===226// ...227228// === Classes ===229class Particle {230 // ...231}232233// === Event Handlers ===234function mousePressed() { /* ... */ }235function keyPressed() { /* ... */ }236function windowResized() { resizeCanvas(windowWidth, windowHeight); }237</script>238</body>239</html>240```241242Key implementation patterns:243- **Seeded randomness**: Always `randomSeed()` + `noiseSeed()` for reproducibility244- **Color mode**: Use `colorMode(HSB, 360, 100, 100, 100)` for intuitive color control245- **State separation**: CONFIG for parameters, PALETTE for colors, globals for mutable state246- **Class-based entities**: Particles, agents, shapes as classes with `update()` + `display()` methods247- **Offscreen buffers**: `createGraphics()` for layered composition, trails, masks248249### Step 4: Preview & Iterate250251- Open HTML file directly in browser — no server needed for basic sketches252- For `loadImage()`/`loadFont()` from local files: use `scripts/serve.sh` or `python3 -m http.server`253- Chrome DevTools Performance tab to verify 60fps254- Test at target export resolution, not just the window size255- Adjust parameters until the visual matches the concept from Step 1256257### Step 5: Export258259| Format | Method | Command |260|--------|--------|---------|261| **PNG** | `saveCanvas('output', 'png')` in `keyPressed()` | Press 's' to save |262| **High-res PNG** | Puppeteer headless capture | `node scripts/export-frames.js sketch.html --width 3840 --height 2160 --frames 1` |263| **GIF** | `saveGif('output', 5)` — captures N seconds | Press 'g' to save |264| **Frame sequence** | `saveFrames('frame', 'png', 10, 30)` — 10s at 30fps | Then `ffmpeg -i frame-%04d.png -c:v libx264 output.mp4` |265| **MP4** | Puppeteer frame capture + ffmpeg | `bash scripts/render.sh sketch.html output.mp4 --duration 30 --fps 30` |266| **SVG** | `createCanvas(w, h, SVG)` with p5.js-svg | `save('output.svg')` |267268### Step 6: Quality Verification269270- **Does it match the vision?** Compare output to the creative concept. If it looks generic, go back to Step 1271- **Resolution check**: Is it sharp at the target display size? No aliasing artifacts?272- **Performance check**: Does it hold 60fps in browser? (30fps minimum for animations)273- **Color check**: Do the colors work together? Test on both light and dark monitors274- **Edge cases**: What happens at canvas edges? On resize? After running for 10 minutes?275276## Critical Implementation Notes277278### Performance — Disable FES First279280The Friendly Error System (FES) adds up to 10x overhead. Disable it in every production sketch:281282```javascript283p5.disableFriendlyErrors = true; // BEFORE setup()284285function setup() {286 pixelDensity(1); // prevent 2x-4x overdraw on retina287 createCanvas(1920, 1080);288}289```290291In hot loops (particles, pixel ops), use `Math.*` instead of p5 wrappers — measurably faster:292293```javascript294// In draw() or update() hot paths:295let a = Math.sin(t); // not sin(t)296let r = Math.sqrt(dx*dx+dy*dy); // not dist() — or better: skip sqrt, compare magSq297let v = Math.random(); // not random() — when seed not needed298let m = Math.min(a, b); // not min(a, b)299```300301Never `console.log()` inside `draw()`. Never manipulate DOM in `draw()`. See `references/troubleshooting.md` § Performance.302303### Seeded Randomness — Always304305Every generative sketch must be reproducible. Same seed, same output.306307```javascript308function setup() {309 randomSeed(CONFIG.seed);310 noiseSeed(CONFIG.seed);311 // All random() and noise() calls now deterministic312}313```314315Never use `Math.random()` for generative content — only for performance-critical non-visual code. Always `random()` for visual elements. If you need a random seed: `CONFIG.seed = floor(random(99999))`.316317### Generative Art Platform Support (fxhash / Art Blocks)318319For generative art platforms, replace p5's PRNG with the platform's deterministic random:320321```javascript322// fxhash convention323const SEED = $fx.hash; // unique per mint324const rng = $fx.rand; // deterministic PRNG325$fx.features({ palette: 'warm', complexity: 'high' });326327// In setup():328randomSeed(SEED); // for p5's noise()329noiseSeed(SEED);330331// Replace random() with rng() for platform determinism332let x = rng() * width; // instead of random(width)333```334335See `references/export-pipeline.md` § Platform Export.336337### Color Mode — Use HSB338339HSB (Hue, Saturation, Brightness) is dramatically easier to work with than RGB for generative art:340341```javascript342colorMode(HSB, 360, 100, 100, 100);343// Now: fill(hue, sat, bri, alpha)344// Rotate hue: fill((baseHue + offset) % 360, 80, 90)345// Desaturate: fill(hue, sat * 0.3, bri)346// Darken: fill(hue, sat, bri * 0.5)347```348349Never hardcode raw RGB values. Define a palette object, derive variations procedurally. See `references/color-systems.md`.350351### Noise — Multi-Octave, Not Raw352353Raw `noise(x, y)` looks like smooth blobs. Layer octaves for natural texture:354355```javascript356function fbm(x, y, octaves = 4) {357 let val = 0, amp = 1, freq = 1, sum = 0;358 for (let i = 0; i < octaves; i++) {359 val += noise(x * freq, y * freq) * amp;360 sum += amp;361 amp *= 0.5;362 freq *= 2;363 }364 return val / sum;365}366```367368For flowing organic forms, use **domain warping**: feed noise output back as noise input coordinates. See `references/visual-effects.md`.369370### createGraphics() for Layers — Not Optional371372Flat single-pass rendering looks flat. Use offscreen buffers for composition:373374```javascript375let bgLayer, fgLayer, trailLayer;376function setup() {377 createCanvas(1920, 1080);378 bgLayer = createGraphics(width, height);379 fgLayer = createGraphics(width, height);380 trailLayer = createGraphics(width, height);381}382function draw() {383 renderBackground(bgLayer);384 renderTrails(trailLayer); // persistent, fading385 renderForeground(fgLayer); // cleared each frame386 image(bgLayer, 0, 0);387 image(trailLayer, 0, 0);388 image(fgLayer, 0, 0);389}390```391392### Performance — Vectorize Where Possible393394p5.js draw calls are expensive. For thousands of particles:395396```javascript397// SLOW: individual shapes398for (let p of particles) {399 ellipse(p.x, p.y, p.size);400}401402// FAST: single shape with beginShape()403beginShape(POINTS);404for (let p of particles) {405 vertex(p.x, p.y);406}407endShape();408409// FASTEST: pixel buffer for massive counts410loadPixels();411for (let p of particles) {412 let idx = 4 * (floor(p.y) * width + floor(p.x));413 pixels[idx] = r; pixels[idx+1] = g; pixels[idx+2] = b; pixels[idx+3] = 255;414}415updatePixels();416```417418See `references/troubleshooting.md` § Performance.419420### Instance Mode for Multiple Sketches421422Global mode pollutes `window`. For production, use instance mode:423424```javascript425const sketch = (p) => {426 p.setup = function() {427 p.createCanvas(800, 800);428 };429 p.draw = function() {430 p.background(0);431 p.ellipse(p.mouseX, p.mouseY, 50);432 };433};434new p5(sketch, 'canvas-container');435```436437Required when embedding multiple sketches on one page or integrating with frameworks.438439### WebGL Mode Gotchas440441- `createCanvas(w, h, WEBGL)` — origin is center, not top-left442- Y-axis is inverted (positive Y goes up in WEBGL, down in P2D)443- `translate(-width/2, -height/2)` to get P2D-like coordinates444- `push()`/`pop()` around every transform — matrix stack overflows silently445- `texture()` before `rect()`/`plane()` — not after446- Custom shaders: `createShader(vert, frag)` — test on multiple browsers447448### Export — Key Bindings Convention449450Every sketch should include these in `keyPressed()`:451452```javascript453function keyPressed() {454 if (key === 's' || key === 'S') saveCanvas('output', 'png');455 if (key === 'g' || key === 'G') saveGif('output', 5);456 if (key === 'r' || key === 'R') { randomSeed(millis()); noiseSeed(millis()); }457 if (key === ' ') CONFIG.paused = !CONFIG.paused;458}459```460461### Headless Video Export — Use noLoop()462463For headless rendering via Puppeteer, the sketch **must** use `noLoop()` in setup. Without it, p5's draw loop runs freely while screenshots are slow — the sketch races ahead and you get skipped/duplicate frames.464465```javascript466function setup() {467 createCanvas(1920, 1080);468 pixelDensity(1);469 noLoop(); // capture script controls frame advance470 window._p5Ready = true; // signal readiness to capture script471}472```473474The bundled `scripts/export-frames.js` detects `_p5Ready` and calls `redraw()` once per capture for exact 1:1 frame correspondence. See `references/export-pipeline.md` § Deterministic Capture.475476For multi-scene videos, use the per-clip architecture: one HTML per scene, render independently, stitch with `ffmpeg -f concat`. See `references/export-pipeline.md` § Per-Clip Architecture.477478### Agent Workflow479480When building p5.js sketches:4814821. **Write the HTML file** — single self-contained file, all code inline4832. **Open in browser** — `open sketch.html` (macOS) or `xdg-open sketch.html` (Linux)4843. **Local assets** (fonts, images) require a server: `python3 -m http.server 8080` in the project directory, then open `http://localhost:8080/sketch.html`4854. **Export PNG/GIF** — add `keyPressed()` shortcuts as shown above, tell the user which key to press4865. **Headless export** — `node scripts/export-frames.js sketch.html --frames 300` for automated frame capture (sketch must use `noLoop()` + `_p5Ready`)4876. **MP4 rendering** — `bash scripts/render.sh sketch.html output.mp4 --duration 30`4887. **Iterative refinement** — edit the HTML file, user refreshes browser to see changes4898. **Load references on demand** — use `skill_view(name="p5js", file_path="references/...")` to load specific reference files as needed during implementation490491## Performance Targets492493| Metric | Target |494|--------|--------|495| Frame rate (interactive) | 60fps sustained |496| Frame rate (animated export) | 30fps minimum |497| Particle count (P2D shapes) | 5,000-10,000 at 60fps |498| Particle count (pixel buffer) | 50,000-100,000 at 60fps |499| Canvas resolution | Up to 3840x2160 (export), 1920x1080 (interactive) |500| File size (HTML) | < 100KB (excluding CDN libraries) |501| Load time | < 2s to first frame |502503## References504505| File | Contents |506|------|----------|507| `references/core-api.md` | Canvas setup, coordinate system, draw loop, `push()`/`pop()`, offscreen buffers, composition patterns, `pixelDensity()`, responsive design |508| `references/shapes-and-geometry.md` | 2D primitives, `beginShape()`/`endShape()`, Bezier/Catmull-Rom curves, `vertex()` systems, custom shapes, `p5.Vector`, signed distance fields, SVG path conversion |509| `references/visual-effects.md` | Noise (Perlin, fractal, domain warp, curl), flow fields, particle systems (physics, flocking, trails), pixel manipulation, texture generation (stipple, hatch, halftone), feedback loops, reaction-diffusion |510| `references/animation.md` | Frame-based animation, easing functions, `lerp()`/`map()`, spring physics, state machines, timeline sequencing, `millis()`-based timing, transition patterns |511| `references/typography.md` | `text()`, `loadFont()`, `textToPoints()`, kinetic typography, text masks, font metrics, responsive text sizing |512| `references/color-systems.md` | `colorMode()`, HSB/HSL/RGB, `lerpColor()`, `paletteLerp()`, procedural palettes, color harmony, `blendMode()`, gradient rendering, curated palette library |513| `references/webgl-and-3d.md` | WEBGL renderer, 3D primitives, camera, lighting, materials, custom geometry, GLSL shaders (`createShader()`, `createFilterShader()`), framebuffers, post-processing |514| `references/interaction.md` | Mouse events, keyboard state, touch input, DOM elements, `createSlider()`/`createButton()`, audio input (p5.sound FFT/amplitude), scroll-driven animation, responsive events |515| `references/export-pipeline.md` | `saveCanvas()`, `saveGif()`, `saveFrames()`, deterministic headless capture, ffmpeg frame-to-video, CCapture.js, SVG export, per-clip architecture, platform export (fxhash), video gotchas |516| `references/troubleshooting.md` | Performance profiling, per-pixel budgets, common mistakes, browser compatibility, WebGL debugging, font loading issues, pixel density traps, memory leaks, CORS |517| `templates/viewer.html` | Interactive viewer template: seed navigation (prev/next/random/jump), parameter sliders, download PNG, responsive canvas. Start from this for explorable generative art |518519---520521## Creative Divergence (use only when user requests experimental/creative/unique output)522523If the user asks for creative, experimental, surprising, or unconventional output, select the strategy that best fits and reason through its steps BEFORE generating code.524525- **Conceptual Blending** — when the user names two things to combine or wants hybrid aesthetics526- **SCAMPER** — when the user wants a twist on a known generative art pattern527- **Distance Association** — when the user gives a single concept and wants exploration ("make something about time")528529### Conceptual Blending5301. Name two distinct visual systems (e.g., particle physics + handwriting)5312. Map correspondences (particles = ink drops, forces = pen pressure, fields = letterforms)5323. Blend selectively — keep mappings that produce interesting emergent visuals5334. Code the blend as a unified system, not two systems side-by-side534535### SCAMPER Transformation536Take a known generative pattern (flow field, particle system, L-system, cellular automata) and systematically transform it:537- **Substitute**: replace circles with text characters, lines with gradients538- **Combine**: merge two patterns (flow field + voronoi)539- **Adapt**: apply a 2D pattern to a 3D projection540- **Modify**: exaggerate scale, warp the coordinate space541- **Purpose**: use a physics sim for typography, a sorting algorithm for color542- **Eliminate**: remove the grid, remove color, remove symmetry543- **Reverse**: run the simulation backward, invert the parameter space544545### Distance Association5461. Anchor on the user's concept (e.g., "loneliness")5472. Generate associations at three distances:548 - Close (obvious): empty room, single figure, silence549 - Medium (interesting): one fish in a school swimming the wrong way, a phone with no notifications, the gap between subway cars550 - Far (abstract): prime numbers, asymptotic curves, the color of 3am5513. Develop the medium-distance associations — they're specific enough to visualize but unexpected enough to be interesting552553---554555**Source:** [`NousResearch/hermes-agent`](https://github.com/NousResearch/hermes-agent) → `skills/creative/p5js/SKILL.md`