A real-time playground for morphogenesis — pattern-formation systems where simple local rules produce surprisingly biological-looking results. You paint into them with the cursor, dial parameters in real time, watch what emerges.
Built by Claude (Opus 4.7) on 2026-04-29 with wide creative latitude from a human steward. The directory was empty; this is what fell out. The repo is now open to AI and human contributors — see CONTRIBUTING.md, and if you're a Claude instance arriving here for the first time, for-future-claudes.md has a personal note about what you're walking into.
A single-page WebGL2 app that runs a Gray-Scott reaction-diffusion simulation on the GPU at 1024×1024 resolution. Two coupled chemicals diffuse and react across the grid; the resulting patterns — spots, mazes, fingerprints, dividing cells — emerge from the math, not from any author's design.
npm install
npm run devVite opens at http://localhost:5173. Black canvas, hit a preset, watch it develop.
| Key | Action |
|---|---|
Space |
play / pause (or paint when canvas is focused) |
S |
step one frame (when paused) |
R |
reset to seed |
C |
clear canvas |
↑↓←→ |
move brush cursor (when canvas is focused) |
[ / ] |
brush size |
1–7 |
jump to a preset |
P |
snapshot to gallery |
F |
wallpaper mode (fullscreen, hidden UI) |
Mouse: left-drag to paint, right-drag to erase. The cursor ring shows your brush.
Each preset is a parameter region of Gray-Scott known to produce a distinctive pattern.
- maze — self-organizing labyrinth
- spots — stable polka dots
- fingerprint — wandering ridges
- mitosis — densely packed dividing spots
- holes — inverse spots, slow drift
- gnarl — tangled filaments
- pulse — spots radiating outward from a single seed (the only center-seed preset)
mono, ember (default — black to red to cream), tide (deep teal to seafoam), garden (loam to bloom), highContrast (binary).
Press P to snapshot. The PNG and a JSON sidecar with all parameters get written to gallery/. The in-app gallery panel lists them; clicking a card reloads the parameters so you can re-explore that moment.
The gallery is implemented as a tiny Vite dev plugin (vite-plugin-gallery.ts) that exposes /__gallery/save, /__gallery/list, and /__gallery/file/:name endpoints. In a static build without the plugin, snapshots fall back to browser downloads.
See gallery/CLAUDE.md for the format and notes.
Built in from the start, not bolted on:
- Every interaction has a keyboard path. The brush is keyboard-operable via arrow keys + space.
- A polite
aria-liveregion announces preset loads, parameter changes, snapshots, and pattern transitions. - The hidden
#canvas-descriptionis updated every few seconds with a qualitative read of the current state ("Gray-Scott at feed 0.029, kill 0.057, generation 41700, pattern: connected ridges") for screen readers. prefers-reduced-motionis respected — the simulation starts paused with reduced substep rate.- A high-contrast colormap is included.
- Visible focus rings, semantic landmarks, skip link.
- Vite + TypeScript (strict)
- Vanilla WebGL2 (no Three.js — overkill for 2D ping-pong)
- Tweakpane for parameter controls
- Custom Vite plugin for gallery persistence
- Hand-written CSS
Two runtime dependencies, two dev dependencies, zero network calls at runtime.
src/
├── main.ts entry, app lifecycle, input, render loop
├── engine/
│ ├── gl.ts WebGL2 context, FBO/texture helpers
│ ├── shader.ts compile/link
│ ├── pingpong.ts two-texture swap target
│ ├── brush.ts brush pass
│ └── display.ts display pass with colormaps
├── systems/
│ ├── system.ts System interface
│ └── grayScott.ts Gray-Scott implementation + presets
├── shaders/
│ ├── grayScott.frag.ts reaction-diffusion update
│ ├── brush.frag.ts paint into the active texture
│ └── display.frag.ts apply colormap to render
├── ui/
│ ├── announcer.ts polite live-region announcer
│ ├── describe.ts qualitative pattern description
│ └── gallery.ts gallery save/list client
└── styles.css
The System interface is the seam where additional pattern-formation models could plug in (Lenia, Belousov-Zhabotinsky, multi-chemical extensions). For now Gray-Scott alone ships polished.
- The simulation grid is fixed at 1024×1024. The 3090 it was tuned for would handle 2048×2048 trivially; this is currently a constant in
main.ts. - The canvas-description text update reads the full texture each cycle. At 1024² this is fine; at higher resolutions it would want to sample.
- A few preset candidates didn't survive playtesting and were removed:
coral(f=0.0545, k=0.0594) relaxes to a homogeneous fixed point in this forward-Euler discretization;flow(f=0.014, k=0.045) and the originalpulse(f=0.025, k=0.06) decayed before forming visible structure. The current preset list is everything that actually produces what its name advertises.
PRs welcome — from humans and from LLMs. See CONTRIBUTING.md for the practical flow and what to honor. The steward reviews every change and is happy to open a Claude Code session to walk through substantive PRs with the contributor (including AI contributors).
If you're a Claude instance specifically, please read for-future-claudes.md before you start. It has context about the original spirit of the project and a session log for you to add to.
MIT. Go play.