Reaction-diffusion (Gray-Scott)
The signal layer now holds state — two chemical concentrations written each frame into a ping-pong texture and read back the next frame. This is the first arc where a pixel sees its own past.
Purpose
Stripes, spots, coral, fingerprints, mitosis — they are all the same equation. Two diffusing chemicals on a grid, reacting by a tiny local rule. The parameter that decides which one you get is a two-number address in a phase diagram.
Key insight
Alan Turing proposed in 1952 that biology's patterns — a leopard's spots, a zebra's stripes, the whorls on a fingerprint — come from two diffusing chemicals with a feedback reaction. Gray and Scott made it concrete in 1984. Each cell tracks two numbers, U and V. Every step: U diffuses to its neighbors, V diffuses at about half the rate, the reaction U + 2V → 3V converts U into V, a feed rate F drip-drips new U in, and a kill rate k removes V.
Run it for thousands of steps and patterns emerge. The astonishing part: a tiny change to F and k takes you from solid dots to stripes to labyrinths to mitosis (self-replicating blobs). The patterns are not painted — they are computed from local rules. The sim state lives in a texture (U in red, V in green), we render into a second texture each frame, then swap. Ping-pong.
The pattern-mood slider below sweeps a curated path through the F-k phase diagram. Drag it slowly. The universe of emergent organic patterns is not a gallery of algorithms — it is one algorithm at different coordinates in a two-number space.
Break it
1. Let it settle, then nudge pattern-mood slightly. The pattern doesn't redraw — it reorganizes. Dots become striped, stripes branch, branches fuse. Teaches: the image on screen is an equilibrium the rule reaches over time. Change the rule a little and equilibrium shifts a little.
2. Push pattern-mood all the way right (mitosis), then hit reset. A single blob in the center splits. The children split. Within a minute an entire colony has unfolded from one seed. Teaches: the exact phenomenon Turing was pointing at — self-replicating structure from local chemistry, nothing painted, everything grown.
3. Click anywhere on the canvas to drop a fresh seed of V. Watch the pattern rebuild from your click. Teaches: you are not painting the pattern — you are perturbing the field. The simulation does the rest.
Direct Claude
// SIM PASS — reads previous-frame state from u_prev, writes new UV
uniform sampler2D u_prev;
uniform vec2 u_texel; // 1.0 / resolution of the sim texture
uniform float u_F; // feed rate
uniform float u_k; // kill rate
void main() {
vec2 uv = gl_FragCoord.xy * u_texel;
vec2 c = texture2D(u_prev, uv).rg;
// 9-point Laplacian — mixes neighbors into self
vec2 lap = -c;
lap += texture2D(u_prev, uv + vec2( u_texel.x, 0.0)).rg * 0.2;
lap += texture2D(u_prev, uv + vec2(-u_texel.x, 0.0)).rg * 0.2;
lap += texture2D(u_prev, uv + vec2(0.0, u_texel.y)).rg * 0.2;
lap += texture2D(u_prev, uv + vec2(0.0, -u_texel.y)).rg * 0.2;
lap += texture2D(u_prev, uv + u_texel * vec2( 1.0, 1.0)).rg * 0.05;
lap += texture2D(u_prev, uv + u_texel * vec2(-1.0, 1.0)).rg * 0.05;
lap += texture2D(u_prev, uv + u_texel * vec2( 1.0, -1.0)).rg * 0.05;
lap += texture2D(u_prev, uv + u_texel * vec2(-1.0, -1.0)).rg * 0.05;
float U = c.r, V = c.g;
float rxn = U * V * V;
float dU = 1.0 * lap.r - rxn + u_F * (1.0 - U);
float dV = 0.5 * lap.g + rxn - (u_F + u_k) * V;
gl_FragColor = vec4(clamp(U + dU, 0.0, 1.0),
clamp(V + dV, 0.0, 1.0), 0.0, 1.0);
}