marbles
Seeded, emergent marble-track generator. A Deno webapp that assembles an endless marble race from Kenney's CC0 Marble Kit, with the Nature Kit as a scenery layer. The URL fragment is the seed — every generated track is a browser-history entry you can share, revisit, and step back/forward through.
Quickstart
deno task dev # build (watch) + serve → http://localhost:8000
Open http:/ — any string is a valid seed.
- New run button (or editing the hash) generates a new seed and history entry.
- Follow cam / Orbit cam or
Ctoggles the camera. Drag to rotate and scroll to zoom. Follow zoom persists; wide views use gentler camera movement. - Pause / Resume or
Kpauses the run while leaving orbit controls usable. - Your marble / Race leader or Space changes the camera subject when the run has other racers. Solo runs omit this control.
- Full screen or
Ftoggles full screen; Esc exits. - Share copies the complete world URL, including seed and settings.
- Settings returns to the launch screen with the current seed and options.
- Simulation options contains Classic loop, Modern/Legacy steering, physical simulation, diagnostics and Piece gallery.
?debug=1overlays the loop centerline;?gallery=1lays out every imported piece.
Tasks
| task | what |
|---|---|
deno task dev |
bundle watch + dev server with live reload |
deno task build |
one-shot browser bundle → static/ |
deno task build:physics |
rebuild Box3D shim → static/ + .wasm |
deno task build:site |
rebuild JS and physics, package static files and assets |
deno task assets:import |
re-run the asset pipeline from the Kenney packs |
deno task test |
full test suite (RNG, registry, topology fuzz, golden seeds) |
deno task check |
type-check everything |
assets/ ships committed (packed GLBs + manifests), so assets:import is only
needed when re-importing from the source packs (path configurable via
--source-root).
Physics shim changes require build:physics as well as the browser build. It
uses Emscripten and CMake; include both native artifacts when distributing the
site. See the stall fix notes for the current
collision behavior and verification.
Deployment
The existing Cloudflare Pages project is marbles, at
marbles-3a0.pages.dev. Build the complete site,
including the native physics module and asset packs, before deploying:
deno task build:site
npx wrangler pages deploy --project-name marbles --branch main
The build output is output/; generated bundles stay out of Git.
How it works
seed (URL fragment)
└─ rngFamily(seed, label) one independent sfc32 stream per system;
├─ topology closed rectilinear cycle (closure by construction)
├─ elevation hill profile, Σ deltas = 0, support-grid heights
├─ style theme weights, palette a/b/c, solid policy
├─ setting scenery pack + placement rule table (Nature Kit)
├─ decor rule-based scenery realization around the track
└─ wfc realization: wave-function collapse over the ring
└─ assemble placements + supports + closed centerline
└─ validateTrack invariants (tests/dev)
Systems emit constraints and weights, never placements; the WFC pass picks the actual pieces (windows over the ring, min-entropy collapse, occupancy vetoes, single-cell fallbacks make contradictions impossible). Streams are derived by label, not draw order, so adding systems later never perturbs existing seeds.
Piece semantics (ports, channel-floor heights, arc radii, ramp tier deltas) were
measured from the GLB geometry; tests/ lints the registry's
internal consistency, and tests/ pins fixed-seed outputs —
regenerating it breaks every seed URL in the wild, so do it knowingly.
Assets
All assets are Kenney's, CC0. The import pipeline packs each kit into a single deduped GLB (162 pieces / 2.0 MB for the Marble Kit) plus a classification manifest. Marble Kit coverage is staged: M1 realizes straights, corners, curves, ramps, slants, tunnels, supports, and decor; bends, s-curves, wide pieces, waves, bumps (M2), helixes, crossings (M3), and splits, funnels, fans-as-obstacles (M4) extend the same window/registry model.