● S4F WebGPU wasm32

Tide

A gallery of four scenes built to show water — open sea, coast, valley and courtyard — of which the first milestone builds the courtyard: a tiled pool with a deep end and a shallow end under a fixed sun, its still surface refracting the tile under it, absorbing more with depth and reflecting the sky. The other three are empty rooms until the milestones that fill them. Not a game: the water is the content.

tide — 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.

the gallery, from the page

Every knob the sample binds to a key, as something a finger can reach.

Natively these are N, M and C, and rows on the pause panel. A phone has none of them. Each button below writes the same cell the key writes and then reads back the name the engine holds, so the labels are never a copy the page keeps of its own.

Clicked the canvas? Press Esc first. The fixture takes the pointer while it is running, the way any mouse-look camera does, and a captured pointer sends every click to the canvas — so nothing above can be reached until the browser hands it back, and Esc is how it does that. Letting the pointer go also pauses the fixture: the status line says Paused until you click the canvas and press Esc again. The knobs change the picture whether it is ticking or not; the orbit camera only turns while it is.
The medium is the one to reach for. Each preset is a point on one model of what light meets in water: pure water's own absorption, which loses red fastest, plus dissolved organic matter, which stains the water brown by eating blue, plus suspended silt, which scatters light back out. The clear pool is pure water, so the deep end reads bluer than the shallow end and the tile stays sharp; the lake adds a trace of both; the pond enough that the floor fades; and the swamp is tannin-black water with little silt in it, which is why it goes dark rather than milky.

what you are looking at

Three things, and each one is a water claim you can check.

The deep end against the shallow end. The two halves of the pool are mirror images seen from the camera's column, so the only thing that separates them is how much water is over the tile. The deep end should read darker and bluer — and under the swamp preset, browner.

The tile through the surface. The lane line on the pool's floor is bent by the water it is seen through: it sits higher in the frame than it would with the pool drained, and the shallow end bends it less than the deep end does.

The coping around it. The water stands a hand's width below the rim, and the stone around the pool is exactly the stone it would be with no water in it at all: the surface is drawn where the pool is and nowhere else.

controls

Nothing to play. There is a camera, and there are the knobs above.

The page opens on the fixed pose the golden frames are taken from, over the clear pool. The orbit camera turns round the pool on the simulation's own fixed step, so it is the same pose at the same tick on every machine.

why this one is worth publishing

Because water is a system, and this is where the system is looked at.

The engine's water is data first: a body is an outline, a level and a medium, and the renderer is one reader of it. This page is that reader in a browser. Press F3 for the receipts: the panel names which geometry path, which binding model and which lighting path this frame was drawn through, which body and medium the frame drew, and what the two water passes — the copy of the frame behind the surface, and the surface itself — cost on this device.

what is actually running

The native sample, with three differences the browser forces.

Start-up is polled across several frames, because requestDevice is a promise and the main thread cannot block on it. The frame loop is driven by requestAnimationFrame out here rather than by a loop inside the engine. And the clock is the browser's, because wasm32-unknown-unknown has no Instant.

There is no fourth difference about saved data: this demo keeps nothing and loads nothing — the courtyard's geometry is built from literals in apps/tide, and the shaders and the font atlas are compiled into the module.

Expect it to be slow on a machine with no GPU. A software WebGPU adapter draws the pool twice over: once as the frame behind the water and once more as the surface that refracts it.
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.