p5js

p5.js sketches: gen art, shaders, interactive, 3D.

  • creative-coding
  • generative-art
  • p5js
  • canvas
  • interactive
  • visualization
  • webgl
  • shaders
  • animation

Declared platforms: linux · macos · windows

Install
npx skills add 'https://github.com/NousResearch/hermes-agent/tree/main/skills/creative/p5js'
Download bundle ↓
main · 24fd22bScanned 2026-09-15

Contributors

GitHub-linked commit authors for this SKILL.md at the saved revision. Co-authors and history before file renames are not included.

File history ↗
View on GitHub
---name: p5jsdescription: "p5.js sketches: gen art, shaders, interactive, 3D."version: 1.0.0author: SHL0MS, Hermes Agentlicense: MITplatforms: [linux, macos, windows]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 ## 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``` 1. **CONCEPT** — Articulate the creative vision: mood, color world, motion vocabulary, what makes this unique2. **DESIGN** — Choose mode, canvas size, interaction model, color system, export format. Map concept to technical decisions3. **CODE** — Write single HTML file with inline p5.js. Structure: globals → `preload()` → `setup()` → `draw()` → helpers → classes → event handlers4. **PREVIEW** — Open in browser, verify visual quality. Test at target resolution. Check performance5. **EXPORT** — Capture output: `saveCanvas()` for PNG, `saveGif()` for GIF, `saveFrames()` + ffmpeg for MP4, Puppeteer for headless batch6. **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: ```html<!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 `python -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: ```javascriptp5.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(dx*dx+dy*dy); // not dist() — or better: skip sqrt, compare magSqlet v = Math.random();        // not random() — when seed not neededlet 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. ```javascriptfunction 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 conventionconst SEED = $fx.hash;              // unique per mintconst 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 determinismlet 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: ```javascriptcolorMode(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: ```javascriptfunction 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: ```javascriptlet 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 shapesfor (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 countsloadPixels();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: ```javascriptconst 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()`: ```javascriptfunction 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. ```javascriptfunction 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 inline2. **Open in browser** — `open sketch.html` (macOS) or `xdg-open sketch.html` (Linux)3. **Local assets** (fonts, images) require a server: `python -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 press5. **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 changes8. **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 Blending1. 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 visuals4. Code the blend as a unified system, not two systems side-by-side ### SCAMPER TransformationTake 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 Association1. 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 3am3. Develop the medium-distance associations — they're specific enough to visualize but unexpected enough to be interesting 
Discovery context

Discovered by repository scan. No exact path reference found in the snapshot’s root AGENTS.md.