Pixel Matter
One height field, three ways of drawing it. The artwork from the home page, with its controls exposed.
The home page. A ridge with a swell drifting through it.
Move a pointer or drag a finger across the field to raise it. Click or tap to send a ring through it.
The concept
A grid of cells sits on a surface that moves slowly. Each cell knows only its height. Everything you see is that one number drawn three ways: as square pixels whose size and colour follow the height, as points, or as ridgelines with the hidden parts removed.
Switching the drawing does not swap one picture for another. The same samples are used throughout, so the surface holds its shape while the way of looking at it changes. Moving the pointer, or a finger, pushes the surface up underneath it.
Under the hood
The model
The height at a point is a sum of a few slow sine waves and one or two moving bumps, plus a bump that follows the pointer. It is a pure function of position and time, which is why the same code can draw the still pictures used for each project on this site: they are this function with different numbers and the clock stopped.
function height(x, y, t, preset, pointer) {
let h = 0;
for (const wave of preset.waves) h += wave.amp * Math.sin(x * wave.fx + y * wave.fy + t * wave.speed);
for (const bump of preset.bumps) h += bump.amp * gaussian(x - bump.x(t), y - bump.y(t), bump.radius);
return h + pointer.strength * gaussian(x - pointer.x, y - pointer.y, pointer.radius);
}Why Canvas 2D and not WebGL
The field is a few thousand axis-aligned squares or a few dozen polylines. Canvas 2D draws that inside a frame on a mid-range phone, starts without compiling shaders, and cannot lose a GPU context. WebGL would add a failure mode and a fallback path to gain speed the picture does not need.
Squares are snapped to whole device pixels so their edges stay crisp. The backing store is capped at twice the CSS size, and the grid gets coarser on small or slow screens instead of dropping frames.
What the pointer leaves behind
The surface under the pointer is pulled toward a bump, and the dent spreads outward as a damped wave that is calm again in about two seconds. It is heavy on purpose: no bounce, and a ring or two at most. The Wave Tank experiment runs the same function with its settings exposed.
When it stops drawing
The loop runs only while the canvas is on screen and the tab is visible. If you ask your system for reduced motion, the clock stays stopped: the field still answers the pointer and the mode switch, one frame at a time. There is a pause button for everyone else.