Quarry
One quarry face, 120 metres across and receding 180, generated from hashed noise and decimated into a hierarchy of clusters. The camera walks down it and back, for ever. Not a game — the geometry is the content, and what it is here to show is a mesh changing detail while you watch and never catching your eye doing it.
what you are looking at
A dolly, a cluster hierarchy, and the seam between them.
The face recedes, and that is the whole reason it is shaped like that. A flat wall sits at one distance from the camera, so every part of it wants the same level of detail and there is nothing for a per-cluster decision to decide. This one runs 180 metres away from you, so the near benches and the far wall of the same mesh sit at screen-space errors an order of magnitude apart.
The camera is the dolly, running on the simulation clock. It covers ninety metres in thirty seconds — three metres a second, a fifth of the speed the free camera flies at — and then turns round and walks back up the face rather than cutting to the start. That is deliberate: a jump back to the beginning would put ninety metres of translation into one frame, which is precisely the artefact this sample exists to say does not happen.
Detail arrives, and it arrives without popping. The hierarchy is a DAG of clusters, each a decimation of the ones below it, and a level is chosen by comparing a group's projected error against a pixel budget. As the camera closes, that error grows and finer geometry is substituted in. The switch has hysteresis on it, so something sitting on the boundary does not flip back and forth between two levels frame after frame — which is what popping is, and which is the thing this whole sample exists to say does not happen.
On this page the choice is made once for the whole face, not once per cluster: see below for why a browser gets that arm and what it costs. So the substitution you are watching for is the face as a whole getting finer as the dolly closes, rather than a patch of rock a few metres ahead changing on its own.
The LOD view is where you can look at the choice directly.
ESC and the LOD VIEW row tints geometry by the level
it was decimated to instead of shading it. Because the level here is chosen
per instance, that is one colour across the whole face rather than the
patchwork a machine with a mesh stage shows — which is the comparison rather
than a shortfall, and the clearest single picture of what the path
difference actually means.
F3 is the receipts. The panel names the geometry path, the
binding model and the lighting path this frame was drawn through, where the
camera is, the triangle count the cut is a reduction of, the pixel budget it
was selected under, and how many instances survived culling — read back off
the GPU a few frames behind, and labelled with the frame they came from
rather than printed as if they were this one's. Against the cluster count it
says no amplification stage on this path, in words, rather than
printing the zero the clearing pass left: on this page there is no
per-cluster stage to count, and a zero there would claim every cluster had
been rejected in a frame that drew all of them.
controls
Nothing to play. There is a camera, and there is a tint.
The page opens already moving, on the dolly. The other two cameras — the fixed pose the golden frames are taken from, and one you fly yourself — are rows on the pause menu rather than keys, because all three share one lens: a free camera with a lens of its own would give you a frame nobody could hold against the reference.
-
Cycle the dolly, the free camera and the fixed golden pose
ESC, then ENTER on
CAMERA -
Tint by decimation level instead of shading
ENTER on
LOD VIEW, same menu - Fly the free camera, once you have swapped to it W A S D, Space and Shift for up and down, the arrows to look
- Pause and resume Esc
- Fullscreen F11
- Debug overlay F3
- Debug console `
- Pause, fullscreen and the debug overlay keys — unless a control for them is listed above
- Debug console, and its on-screen keyboard the CONSOLE button, once you have touched the canvas
why this one is worth publishing
Because a browser can only draw it one way, and that is the point.
The engine picks its geometry path from what the device offers, and it has
three. On a machine with a mesh stage it selects a level of detail
per cluster, so one mesh can be drawn from several levels at once
across its own surface. WebGPU exposes no mesh shader and no GPU-side draw
count, so this page takes the third arm —
GeometryPath::IndirectPerBatch — with no branch anywhere for it
to take instead. That means the level is chosen once per instance
and the whole face is drawn at one level at a time.
That is the honest picture of what a browser visitor gets, and it is the reason the page is worth having: the detail still arrives as the camera closes, the switch is still hysteretic, and the LOD view still shows you which level you are looking at — one flat tint rather than a patchwork, which is the comparison rather than a shortfall. Open the console and the heartbeat names the arm it resolved to once a second, so you do not have to take that on trust.
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, because there is nothing to keep. It loads nothing either — the face is generated from a hash of each lattice coordinate at start-up rather than read off a disk, so it is the same rock on every machine, and the shaders and the font atlas are compiled into the module.
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.