A remake of the spirit of KPT Bryce — the beloved 1990s 3D landscape generator — built in Rust, compiled to WASM, rendered with WebGPU, and running entirely in the browser. A Raspberry Pi 5 with 4 GB of RAM is a first-class, release-gating target: if it doesn't run there, it doesn't ship.
The workspace running on a Pi 5's own V3D GPU: Create/Edit/Sky palettes,
context-sensitive preset rail, live viewport, status bar.
- KPT Bryce: https://en.wikipedia.org/wiki/Bryce_(software)
- Bryce 7 Pro / UltraScenery 2: https://www.daz3d.com/bryce-7-pro
- A video of Bryce 1: https://www.youtube.com/watch?v=MY8GPU5osx4
- Deploy in the browser
- Has basic generative terrain generation
- Has nice UX similar to the original Bryce
- Can run on a low scale Raspi5 or similar in 4GB ram or less
- Use Browser WebGPU
- Four generators — ridged multifractal, fBm, billow, diamond-square — computed in WGSL compute shaders (validated bit-close against CPU references), regenerating live as you drag sliders.
- Erosion: thermal (talus-gated) and hydraulic (simplified transport) GPU kernels; brush sculpting with shift+click (raise) / ctrl+click (carve). Every erosion run and brush stroke is an undoable command.
- 16-bit grayscale heightmap PNG import/export.
![]() |
![]() |
|---|---|
| Terrain Lab: generator stack + filters, viewport as live preview | After 3× hydraulic erosion — sediment fills the valleys |
- Analytic sky dome with a draggable sun dome widget, wind-scrolled 2D clouds, and distance fog. Four presets (clear-day, dusk, night, overcast) are starting points — the Sky Lab designs every parameter: sun brightness and color, horizon/zenith colors, fog color and density, and cloud coverage/scale/softness. Custom skies detach from their preset and save with the document.
- Shadow mapping: terrain and objects cast and receive sun shadows.
- One
set_skycommand relights the entire scene — sun, fog, sky, objects.
![]() |
![]() |
|---|---|
| Dusk preset | Sky Lab: custom clouds, fog, and sky colors |
- Four-layer procedural materials blended by altitude and slope windows; presets: verdant, desert, volcanic, arctic.
![]() |
![]() |
|---|---|
| Desert preset retracks eroded geometry automatically | Material Lab: layer windows visualized |
- Scene graph with terrain, water planes, ground plane, primitives (sphere/cube/cylinder/cone/torus), point lights, cameras — all placed, transformed, hidden, locked, duplicated, and deleted through undoable commands (depth 256).
- Analytic click picking (ray vs. exact shapes + heightfield march), selection highlighting, arrow-key nudging.
- Bryce-style workspace: three mode palettes, preset rail, full-screen
Labs (live preview + Apply/Cancel via the undo stack), objects
drawer, toasts,
?shortcut overlay. - Autosave + crash recovery: the scene autosaves to origin-private OPFS storage every 60 s and on tab-hide; after a crash, the next launch offers to restore.
- Documents save/load as versioned
.burysuJSON.
The disaster drill: SIGKILL the browser, relaunch, restore the autosave.
- A REPL console (
`) and a natural-language bar (/) that both speak one command language:terrain mountains scale=large,water level=deep,move #3 to 1 0 2,list,undo— or just "mountains and a lake at dusk". The natural-language tier is a keyword engine (no LLM, microseconds, zero RAM); optional LLM backends layer on top emitting the same DSL. - Semantic, scene-relative vocabulary:
water level=valley_floorresolves to the 30th percentile of the actual terrain heights, not a fixed number. - Macro recorder: every discrete edit — typed, clicked, or applied in a lab
— is echoed to a transcript you can save as a replayable
.musescript and load back. - External control surface: open the app with
?repl=ws://host:portand an outside program drives the engine over WebSocket — one line in, one structured JSON result out (with thecreatedids to build-then-manipulate). Full protocol and vocabulary in docs/CONSOLE.md.
- Rust →
wasm32-unknown-unknownwithwgpu; the same crates run natively for headless GPU tests (noise/erosion kernels validated against CPU references, offscreen golden-pixel rendering). - A frozen JSON command/event/query API (FSD §6.3) is the only way anything mutates the document — the Svelte UI speaks it today, the Muse natural-language assistant will speak it next (see docs/ASSISTANT.md).
- Runs on the Pi 5's V3D GPU via WebGPU compatibility mode with a readback presentation fallback, and survives that driver's quirks by construction — see the war stories below.
Prerequisites: Rust (stable) with the wasm32-unknown-unknown target,
wasm-pack, and Node ≥ 18
(nvm install --lts works; the build script finds nvm installs).
./scripts/build-web.sh # wasm-pack + vite build into app/dist (+ size gate)
python3 -m http.server -d app/dist 8080 # open http://localhost:8080Development loop:
./scripts/check.sh # full gate: fmt, clippy, tests, wasm build, size budget,
# headless-browser smoke test
cd app && npm run dev # Vite dev server for UI work (expects app/pkg from wasm-pack)WebGPU on Linux Chromium is behind flags: enable #enable-unsafe-webgpu
(and #enable-vulkan) in chrome://flags.
The Pi needs an upstream-configured Chromium (the distro build has no working WebGPU backend) and two URL parameters:
chrome --use-angle=gles --enable-unsafe-webgpu --use-webgpu-adapter=opengles \
--enable-features=WebGPUCompatibilityMode \
'http://<host>:8080/?compat=1&present=readback'Full setup, measurements, and the reasons behind every flag: docs/pi5-findings.md and bench/PI5-SPIKE.md.
- Functional Specification (FSD) — scope, requirements, architecture, budgets, risks
- UX Plan — design language, application anatomy, labs, flows, accessibility
- Console & Control Language — how to use the REPL, the full command vocabulary,
.musemacros, and the WebSocket external-control protocol - Assistant Design — the "Muse": a REPL-first scene DSL (Poplog-inspired), text/STT intent parsing, tiered optional LLM backends, camera flyby
- Implementation Roadmap — stack decision, repo layout, milestones
- WebGPU Platform Notes — the bug catalog from bring-up: x86 vs Pi, driver bugs, workarounds, testing traps
- Pi 5 Findings — the Pi deep-dive with screenshots
| Milestone | ||
|---|---|---|
| M0 | Foundation & Pi 5 spike | ✅ |
| M1 | Terrain core (GPU noise, camera, PNG I/O) | ✅ |
| M2 | Document model, command API, undo, picking | ✅ |
| M3 | Sky, shadows, clouds, materials, erosion | ✅ |
| M4 | Svelte workspace, Labs, gizmos, autosave/recovery | 🔨 build complete; UX hallway test remaining |
| M-AI | Muse assistant (Scene Intent DSL, REPL, LLM tiers) & flyby | 🔨 Tier 0 + REPL/control surface done; LLM tiers + flyby remain |
| M5 | Progressive ray-traced final renderer | — |
| M6 | Hardening & 1.0 | — |
See FSD §7. In practice today: 38+ native tests (unit, GPU-vs-CPU kernel validation on any adapter including CI's lavapipe, offscreen pixel assertions), a headless-browser smoke test that catches Dawn-only shader failures, and scripted-click verification on a physical Pi 5 with screenshot pixel checks — because a running frame loop is not evidence of visible output (we learned that the hard way).
- On Pi 5 / Linux-GLES, Chromium cannot composite WebGPU canvases yet, so
the app presents through a readback path (
?present=readback) at ~30 fps; native swapchain presentation returns when the upstream fix lands. - The V3D driver misreads multi-slot uniform structs and silently drops some
draws; all shipped shaders follow defensive layout rules
(details).
diag.html(served from the app) runs a 30-test WebGPU bisect on any suspect device. - Hydraulic erosion is a simplified transport model (full water/sediment pipe model planned with renderer polish).





