Preset
7.0 per second
10 cells per second
8.0

What the home page uses: heavy damping, so a press eases away without a ripple.

Press and drag across the surface, or use the button. The step is 0.11 cells of wave travel; the scheme stays stable below 0.7, so no setting here can blow it up.

The concept

When the pointer presses the surface on the home page, the dent does not simply fade. Each cell is pulled toward the average of its four neighbours, which carries the dent outward. It is pulled back to flat by a weak spring, and slowed by damping. Three numbers decide the whole character: how fast the wave travels, how hard it is slowed, and how strongly it is pulled back.

This tank runs the same function the artwork runs, with those three numbers on sliders. The first preset is the exact setting the home page uses. It is heavy on purpose, so a press eases away without bouncing. The lighter presets show what that choice turned down.

Under the hood

Two passes, not one

The force on every cell is worked out from one snapshot of the surface, and only then does anything move. The first version of this simulation updated cells in place while their neighbours were still being read, and it blew up within a second. A unit test now runs the wave for ten seconds at four frame rates and fails if it ever stops being a number or grows past a bound.

The step, in two loops
for (const cell of cells) velocity[cell] += force(cell, displacement) * dt;
for (const cell of cells) displacement[cell] += velocity[cell] * dt;
Why no setting can break it

This scheme is stable while the wave travels less than about seven tenths of a cell per step. Steps are never longer than a ninetieth of a second and the speed slider stops at 30 cells a second, so the product stays near a third. The readout beside the sliders shows the number for the setting you chose.

When it stops

The loop runs only while the surface is being pressed or is still moving, and stops when the largest remaining motion falls under a small threshold. Nothing runs on its own, which is also why it needs no pause button.

Keep exploring