What it is
A probabilistic subsurface viewer for Kestrel Ridge, a fictional gold prospect on a real range front in central Nevada. The first act is a CesiumJS globe that flies from orbit to the 2 × 2 km block, drawn on real 3D terrain with its twelve boreholes. "Go underground" hands off to a Three.js view of the same block from the same angle: rock units interpolated between the holes, the probability that each 50 m cell holds gold above a chosen cut-off grade, where the model is only guessing, and the next hole that would settle the most rock. Drill it and the model is re-kriged with the new data.
Everything below the ground is synthetic and says so on the page. The point is the method and the honesty of the picture, not a deposit.
Data
- Block terrain: real heights from the open AWS Terrain Tiles (Terrarium encoding; sources include USGS 3DEP), baked by
scripts/subsurface-dem.mjs into a 129 × 129 grid (15.6 m spacing, decimetre precision, 44 KB) so the underground view needs no network. - Globe terrain: the same tiles streamed at runtime through a
CustomHeightmapTerrainProvider, resampled from Web Mercator to Cesium's geographic tiles. Real heights are fetched only for close-in tiles around the prospect; the rest of the planet stays on the ellipsoid, which is invisible from that far out and saves hundreds of requests. No Cesium ion token is used. - Imagery: Esri World Imagery over Cesium's bundled Natural Earth II, lit by a fixed morning sun so the relief reads.
- The hidden geology is a seeded model (
core/geology.ts): alluvium in the valleys, a welded tuff dipping east, a limestone host, shale, and a granodiorite intrusion doming up under a skarn gold shoot at the limestone contact, plus a smaller structural lens to the north-west. Holes are "drilled" into it every 10 m (core/holes.ts): a rock unit and an assay with lognormal lab and nugget scatter. The viewer only ever sees those logs.
Engineering decisions
- Kriging, written from scratch and unit-tested (
core/krige.ts, core/model.ts). Contact surfaces use ordinary kriging on the depths picked from the logs, with the alluvium kriged as a thickness so holes that start in bedrock count as zero. Grade uses simple kriging of log-grade with an anisotropic exponential covariance (650 m across, 200 m vertical) and a moving neighbourhood: the five nearest samples of each hole, found by projecting onto the hole's line, then the fourteen nearest of those. A Cholesky solve per voxel gives the estimate and its kriging variance. - P(grade ≥ cut-off) per voxel follows from the lognormal estimate and its SD. Changing the cut-off only recomputes that step, on the main thread, in a few milliseconds.
- The next hole scores every collar on a 200 m grid with five orientations by Σ P·(1 − P) along the path. That term peaks where the model is closest to a coin toss, so the winner settles the most rock rather than chasing the richest guess. The page reports it as metres of undecided rock.
- After the first build the mean and variance (the variogram) stay fixed, as a geostatistician would after modelling them once. New holes add data but do not move the yardstick, so "model doubt" (mean kriging SD as a share of the no-data SD) only falls as you drill.
- The model is built in a Web Worker (about 0.9 s on a laptop for 40 × 40 × 33 voxels) with the typed arrays transferred back; the drilling animation runs while it works.
- The underground scene is plain Three.js with hand-written GLSL. Walls and the cut face read the kriged contacts from half-float textures per pixel, so the geology on the section is exact, not a texture. A screen-space hatch marks ±1 SD around each contact. The ore and doubt volumes are raymarched from a 3D texture with Beer–Lambert opacity (independent of the step count) and gradient shading. Boreholes are merged geometry: a lithology core plus a downhole bar chart of grade.
- The hand-off: the globe's last flight ends at the same heading, pitch and range the Three.js camera starts from, then the canvases cross-fade and the section sweeps in.
- Cesium is the engine-only package (
@cesium/engine and @cesium/core, no widgets). Its workers, wasm decoders and textures are copied to /vendor/cesium/ at build time. Its render loop is off while you are underground, and both engines stop when the page is hidden or scrolled away.
Performance and accessibility
- All text is HTML: masthead, brief, controls, legend, model figures, strip logs and a description of the scene. Both canvases are hidden from assistive technology.
- Keyboard: Enter goes underground, M returns, arrow keys turn, + and − zoom, 1–3 pick the view, [ and ] move the section, D drills, N cycles the proposals, G ghosts the ground, Escape closes the log.
- Reduced motion: no flights (the globe opens on the site), the cut and the drilling appear in their final state, nothing pulses.
- The loader stays until the globe's terrain and imagery for the site have loaded behind it and the fonts are in. The globe preloads the site view first, then jumps out to orbit, so the opening flight lands on a finished view.
- If the globe cannot start, the viewer opens underground. If the terrain file is missing, a stand-in surface is used and the footnote says so.