● post-MVP WebGPU wasm32

Tumble

The physics engine, one rung at a time, on the scene built to prove each one — and, where a rung has not landed yet, the gap it leaves, said out loud.

tumble — crcbl
Starting…
Click the canvas first. A canvas has to hold keyboard focus to receive keys, and browsers will not start audio until you have interacted with the page. Clicking away pauses the game and lets go of whatever key was held — no platform sends the release for a key that was down when focus left.
Touch reaches the engine. A contact arrives as an ordinary pointer, and the first one is also the gesture a browser waits for before it will start audio. Whether this demo can be played with one is in its controls below — not all of them can. Switching tabs pauses the game and lets go of whatever was held: no platform sends the release for an input that was down when focus left.

controls

It runs itself. The keys only pick the room.

what is actually running

A T-handle in zero gravity, flipping.

The orange handle is spun about its middle axis — the one whose moment of inertia sits between the other two — with a tiny nudge about another. That spin is unstable, and the handle turns itself over and back again for as long as the page is open. It is the Dzhanibekov effect, and nothing on this page drives it: it falls out of the inertia tensor the two boxes add up to and the gyroscopic term in the rotation equations.

The counters are the claim. A free body keeps its angular momentum and its energy, and a simulation that did not would show it here as a handle slowly settling into a steady spin. The integrator solves the gyroscopic step by the implicit midpoint rule, which conserves both, and the panel shows how far each has drifted, in trillionths.

A box dropped flat, landing flat.

The blue box is dropped with no spin and lands on its four lower corners, level to a trillionth, sunk a fraction of a millimetre into the floor: every contact is a stiff spring, and the box's weight compresses it. The box is put back at the top every four seconds to land again.

The obstacle wall.

Balls, and every seventh drop a pill, fall onto a board of pegs, tilted bars and turned wedges between a back board and an invisible front pane, and bounce down into the bins. The counters are what the contact solver did: pairs the broadphase is tracking, contacts begun and ended, the time each stage took, the deepest overlap, and the mean bounce against the restitution the materials asked for. The wall holds a hundred and twenty bodies and takes the oldest away as each new one drops; every fifth drop is a cube.

A thousand balls in a pit.

Waves of five balls pour into a walled pit until there are a thousand, and the pile comes to a stop and falls asleep: a resting island costs the solver almost nothing, and the panel counts the awake and sleeping bodies. The pit neither overflows nor clears, which is a later rung's scene. The step times on the panel read "no clock" here, because the page's physics reads none; the browser's figure is the whole frame's, timed around the call from outside and logged to the console as a [TIMING] line.

The hash is the same everywhere. Nothing in a physics step calls a platform's maths library: the operations it uses are ones every processor performs identically, and the sine and cosine it needs are built inside the engine from them. So the state hash all the rooms reach ten seconds in — whichever room is on screen — is the very number the native build's test pins, and the browser test checks the two agree.

Open the console for the engine's log. The wasm module imports nothing, not even console.log, so log lines are queued inside wasm and drained by the page each frame. crcbl.audio(), crcbl.saves() and crcbl.logLevel(4) are there too.