๐Ÿ“„ SKILL.md

โ† Vault

name: p5js

description: "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. 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."

version: 1.0.0

metadata:

hermes:

tags: [creative-coding, generative-art, p5js, canvas, interactive, visualization, webgl, shaders, animation]

related_skills: [ascii-video, manim-video, excalidraw]


p5.js Production Pipeline

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

Stack

Single self-contained HTML file per project. No build step required.

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

`

1. CONCEPT โ€” Articulate the creative vision: mood, color world, motion vocabulary, what makes this unique

2. DESIGN โ€” Choose mode, canvas size, interaction model, color system, export format. Map concept to technical decisions

3. CODE โ€” Write single HTML file with inline p5.js. Structure: globals โ†’ preload() โ†’ setup() โ†’ draw() โ†’ helpers โ†’ classes โ†’ event handlers

4. PREVIEW โ€” Open in browser, verify visual quality. Test at target resolution. Check performance

5. EXPORT โ€” Capture output: saveCanvas() for PNG, saveGif() for GIF, saveFrames() + ffmpeg for MP4, Puppeteer for headless batch

6. VERIFY โ€” Does the output match the concept? Is it visually striking at the intended display size? Would you frame it?

Creative Direction

Aesthetic Dimensions

Per-Project Variation Rules

Never use default configurations. For every project:

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:

    `javascript

    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:

    `javascript

    // In draw() or update() hot paths:

    let a = Math.sin(t); // not sin(t)

    let r = Math.sqrt(dxdx+dydy); // 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.

    `javascript

    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:

    `javascript

    // 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:

    `javascript

    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:

    `javascript

    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:

    `javascript

    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:

    `javascript

    // 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:

    `javascript

    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():

    `javascript

    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.

    `javascript

    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:

    1. Write the HTML file โ€” single self-contained file, all code inline

    2. Open in browser โ€” open sketch.html (macOS) or xdg-open sketch.html (Linux)

    3. Local assets (fonts, images) require a server: python3 -m http.server 8080 in the project directory, then open http://localhost:8080/sketch.html

    4. Export PNG/GIF โ€” add keyPressed() shortcuts as shown above, tell the user which key to press

    5. Headless export โ€” node scripts/export-frames.js sketch.html --frames 300 for automated frame capture (sketch must use noLoop() + _p5Ready)

    6. MP4 rendering โ€” bash scripts/render.sh sketch.html output.mp4 --duration 30

    7. Iterative refinement โ€” edit the HTML file, user refreshes browser to see changes

    8. Load references on demand โ€” use skill_view(name="p5js", file_path="references/...") to load specific reference files as needed during implementation

    Performance Targets

    References

    ModeInputOutputReference
    --------------------------------
    Generative artSeed / parametersProcedural visual composition (still or animated)references/visual-effects.md
    Data visualizationDataset / APIInteractive charts, graphs, custom data displaysreferences/interaction.md
    Interactive experienceNone (user drives)Mouse/keyboard/touch-driven sketchreferences/interaction.md
    Animation / motion graphicsTimeline / storyboardTimed sequences, kinetic typography, transitionsreferences/animation.md
    3D sceneConcept descriptionWebGL geometry, lighting, camera, materialsreferences/webgl-and-3d.md
    Image processingImage file(s)Pixel manipulation, filters, mosaic, pointillismreferences/visual-effects.md ยง Pixel Manipulation
    Audio-reactiveAudio file / micSound-driven generative visualsreferences/interaction.md ยง Audio Input
    LayerToolPurpose
    ----------------------
    Corep5.js 1.11.3 (CDN)Canvas rendering, math, transforms, event handling
    3Dp5.js WebGL mode3D geometry, camera, lighting, GLSL shaders
    Audiop5.sound.js (CDN)FFT analysis, amplitude, mic input, oscillators
    ExportBuilt-in saveCanvas() / saveGif() / saveFrames()PNG, GIF, frame sequence output
    CaptureCCapture.js (optional)Deterministic framerate video capture (WebM, GIF)
    HeadlessPuppeteer + Node.js (optional)Automated high-res rendering, MP4 via ffmpeg
    SVGp5.js-svg 1.6.0 (optional)Vector output for print โ€” requires p5.js 1.x
    Natural mediap5.brush (optional)Watercolor, charcoal, pen โ€” requires p5.js 2.x + WEBGL
    Texturep5.grain (optional)Film grain, texture overlays
    FontsGoogle Fonts / loadFont()Custom typography via OTF/TTF/WOFF2
    DimensionOptionsReference
    -------------------------------
    Color systemHSB/HSL, RGB, named palettes, procedural harmony, gradient interpolationreferences/color-systems.md
    Noise vocabularyPerlin noise, simplex, fractal (octaved), domain warping, curl noisereferences/visual-effects.md ยง Noise
    Particle systemsPhysics-based, flocking, trail-drawing, attractor-driven, flow-field followingreferences/visual-effects.md ยง Particles
    Shape languageGeometric primitives, custom vertices, bezier curves, SVG pathsreferences/shapes-and-geometry.md
    Motion styleEased, spring-based, noise-driven, physics sim, lerped, steppedreferences/animation.md
    TypographySystem fonts, loaded OTF, textToPoints() particle text, kineticreferences/typography.md
    Shader effectsGLSL fragment/vertex, filter shaders, post-processing, feedback loopsreferences/webgl-and-3d.md ยง Shaders
    CompositionGrid, radial, golden ratio, rule of thirds, organic scatter, tiledreferences/core-api.md ยง Composition
    Interaction modelMouse follow, click spawn, drag, keyboard state, scroll-driven, mic inputreferences/interaction.md
    Blend modesBLEND, ADD, MULTIPLY, SCREEN, DIFFERENCE, EXCLUSION, OVERLAYreferences/color-systems.md ยง Blend Modes
    LayeringcreateGraphics() offscreen buffers, alpha compositing, maskingreferences/core-api.md ยง Offscreen Buffers
    TexturePerlin surface, stippling, hatching, halftone, pixel sortingreferences/visual-effects.md ยง Texture Generation
    FormatMethodCommand
    -------------------------
    PNGsaveCanvas('output', 'png') in keyPressed()Press 's' to save
    High-res PNGPuppeteer headless capturenode scripts/export-frames.js sketch.html --width 3840 --height 2160 --frames 1
    GIFsaveGif('output', 5) โ€” captures N secondsPress 'g' to save
    Frame sequencesaveFrames('frame', 'png', 10, 30) โ€” 10s at 30fpsThen ffmpeg -i frame-%04d.png -c:v libx264 output.mp4
    MP4Puppeteer frame capture + ffmpegbash scripts/render.sh sketch.html output.mp4 --duration 30 --fps 30
    SVGcreateCanvas(w, h, SVG) with p5.js-svgsave('output.svg')
    MetricTarget
    ----------------
    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 resolutionUp to 3840x2160 (export), 1920x1080 (interactive)
    File size (HTML)< 100KB (excluding CDN libraries)
    Load time< 2s to first frame
    FileContents
    ----------------
    references/core-api.mdCanvas setup, coordinate system, draw loop, push()/pop(), offscreen buffers, composition patterns, pixelDensity(), responsive design
    references/shapes-and-geometry.md2D primitives, beginShape()/endShape(), Bezier/Catmull-Rom curves, vertex() systems, custom shapes, p5.Vector, signed distance fields, SVG path conversion
    references/visual-effects.mdNoise (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.mdFrame-based animation, easing functions, lerp()/map(), spring physics, state machines, timeline sequencing, millis()-based timing, transition patterns
    references/typography.mdtext(), loadFont(), textToPoints(), kinetic typography, text masks, font metrics, responsive text sizing
    references/color-systems.mdcolorMode(), HSB/HSL/RGB, lerpColor(), paletteLerp(), procedural palettes, color harmony, blendMode(), gradient rendering, curated palette library
    references/webgl-and-3d.mdWEBGL renderer, 3D primitives, camera, lighting, materials, custom geometry, GLSL shaders (createShader(), createFilterShader()), framebuffers, post-processing
    references/interaction.mdMouse events, keyboard state, touch input, DOM elements, createSlider()/createButton(), audio input (p5.sound FFT/amplitude), scroll-driven animation, responsive events
    references/export-pipeline.mdsaveCanvas(), saveGif(), saveFrames(), deterministic headless capture, ffmpeg frame-to-video, CCapture.js, SVG export, per-clip architecture, platform export (fxhash), video gotchas
    references/troubleshooting.mdPerformance profiling, per-pixel budgets, common mistakes, browser compatibility, WebGL debugging, font loading issues, pixel density traps, memory leaks, CORS
    templates/viewer.htmlInteractive 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

    1. Name two distinct visual systems (e.g., particle physics + handwriting)

    2. Map correspondences (particles = ink drops, forces = pen pressure, fields = letterforms)

    3. Blend selectively โ€” keep mappings that produce interesting emergent visuals

    4. 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

    1. Anchor on the user's concept (e.g., "loneliness")

    2. 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

    3. Develop the medium-distance associations โ€” they're specific enough to visualize but unexpected enough to be interesting