Skip to content

Browser app: wasm facade, Web Worker simulation, WebGL2 renderer - #4

Merged
mahim37 merged 10 commits into
mainfrom
web-app
Sep 20, 2026
Merged

mahim37 merged 10 commits into
mainfrom
web-app

Conversation

@mahim37

@mahim37 mahim37 commented Sep 6, 2026 •

Copy link
Copy Markdown
Owner

Stacked on the core PR (core-sim). Merge that first; GitHub retargets this PR to main.

What is in

  • crates/rustywings-wasm: a thin Sim over World. Config crosses as JSON text (serde_wasm_bindgen silently drops unknown keys; serde_json honours deny_unknown_fields, so a typo in a shared link is an error that names the field). Seeds as hex strings, ids and ticks as f64. frame.rs packs render state into one flat f32 buffer and is tested natively.
  • web/: Vite + TypeScript, no framework.
    • Simulation on a Web Worker: fixed-timestep accumulator (1×/4×/16×/max), stats sampled every 25 ticks, frames posted into two recycled transferable buffers. A slow machine slows the world; it never freezes the page.
    • WebGL2 renderer: patches, seeds, vision wedges, illustrated bird sprites (Canvas-drawn atlas, four wing frames) and the selection ring in five instanced draw calls. The camera wraps on the torus; drag to pan, wheel to zoom, double-click to reset.
    • SVG charts with a linked crosshair and table view, windowed over 5k/25k/100k ticks; KPI tiles with 300-tick trends; field-of-view trend with eye wedges.
    • Inspector: portrait, energy, facts, body traits against their bounds, live retina activation, hidden-layer weight heatmap, Follow, Save genome.
    • Four plain-language sliders that write into the real config and apply live through World::set_config; Reset; Copy config for rustywings run --config.
    • Snapshot and rewind, scatter seeds, click-to-aim meteor, random bird, keyboard shortcuts, help with credits.
    • ?seed=<hex>[&cfg=<base64url diff>] share links. A link whose settings are rejected falls back to the defaults with a toast naming the bad field.
  • Core: World::set_config (validated, brain shape fixed, grids rebuilt). Defaults and the golden checksum are unchanged.
  • CI: a web job (wasm-pack, tsc, vitest, vite build); the wasm crate is linted natively and on wasm32.
  • Deploy: root vercel.json with web/scripts/vercel-install.sh and vercel-build.sh, which install rustup and wasm-pack on Vercel's image and build from source. Nothing compiled is committed. The scripts were run end to end in a Linux Node container.
  • README rewritten for the app; the web package gets its own README.

Verified

  • cargo fmt --check, cargo clippy -D warnings (native and wasm32), cargo test --workspace.
  • pnpm check, pnpm test (24 tests), pnpm build. The vitest suite loads the built wasm in Node and asserts the golden checksum 1065c218b08af910 for seed 1 at tick 2000, the value the Rust tests pin and rustywings verify prints.
  • Headless Chromium over the DevTools protocol: worker, wasm and WebGL2 run; snapshot, rewind, meteor, sliders to URL, scatter, share, help and the bad-link fallback exercised. Max speed about 740 ticks/s at 590 birds and about 200 ticks/s at 2,300 birds, rendering at 60 fps independently.
  • wasm binary 234 KB (102 KB gzipped); JS 47 KB plus a 10 KB worker.

Action needed at merge time

In the Vercel project settings, change Root Directory from www to the repository root. Until then Vercel keeps building the legacy app from www/ and ignores the new vercel.json, which is why the Vercel check on this PR fails. The first build takes a few minutes longer while rustup and wasm-pack install.

Later

Snapshot file import/export and replay, family tree, trails, performance work (sensing every other tick, SIMD), a sweep subcommand, README GIFs, removal of libs/ and www/.

@vercel

vercel Bot commented Sep 6, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
rusty-wings Error Error Sep 6, 2026 11:06am UTC

World::set_config validates the new config, refuses a change of brain
shape (it fixes the genome length), rebuilds the spatial hashes when the
grid resolution changes and otherwise takes effect from the next tick.
This is what live sliders in a UI call. A rejected config leaves the
world untouched.
A thin Sim over World. Config crosses as JSON text: serde_wasm_bindgen
silently drops unknown keys, whereas serde_json honours deny_unknown_fields,
so a typo in a shared link is an error that names the field rather than a
silent default. Seeds cross as hex strings, ids and ticks as f64. Stats,
inspections and config come back as plain objects.

frame.rs packs render state into one flat f32 buffer (a four-float header,
eight floats per bird, two per seed and per patch centre) so a worker can
hand a frame to the page in one transfer. It is tested natively.
No framework. Strict TypeScript, vitest, and a Vite config that builds
the simulation worker as an ES module so the wasm-bindgen glue can locate
its binary, and injects the wasm size and build SHA for the status bar.
wasm-pack output lands in src/wasm and is not committed.
The worker owns the wasm Sim. A fixed-timestep accumulator runs it at 1x,
4x or 16x real time, or flat out within a 12 ms budget per turn; stats are
sampled every 25 ticks regardless of speed; frames are posted into one of
two recycled transferable buffers, so nothing is allocated in steady state
and a slow machine slows the world instead of freezing the page.

protocol.ts is the message contract and mirrors the frame layout from
frame.rs. The vitest suite loads the built wasm in Node and asserts the
golden checksum for seed 1 at tick 2000, the value pinned in the Rust
tests.
Five instanced draw calls per frame: seed patches, seeds, vision wedges,
birds and the selection ring, all reading from the packed frame buffer.
Birds are drawn once with Canvas 2D into a four-frame atlas per species;
the shader picks a wing position from time and a per-bird phase. The
camera is a centre and a zoom on the unit torus, with positions wrapped in
the vertex shader so panning across an edge is seamless.
SVG line charts with a linked crosshair, end labels, an area fill and a
table view; a history of sampled stats served as windowed, bucket-averaged
series over the last 5k, 25k or 100k ticks; the "Meet a bird" panel with
portrait, energy, body traits against their bounds, live retina activation
and the hidden-layer weight heatmap; four plain-language sliders that write
into the real config.
Layout: transport and speed in the header, the world on the left, KPI
tiles, population and field-of-view charts, the inspector and settings on
the right, a status bar with ups, fps and the world checksum. Drag to pan,
wheel to zoom, click to select, click-to-aim meteor, snapshot and rewind,
scatter seeds, keyboard shortcuts, help.

State that matters lives in the URL: ?seed=<hex> plus a base64url JSON
diff of any settings that differ from the defaults, so the same link
replays the same world. A link whose settings are rejected falls back to
the defaults and says which field was wrong.
The web job installs wasm-pack, builds the wasm, type-checks, runs vitest
(which loads the built wasm and checks the golden checksum) and builds the
site. The wasm-bindgen facade only compiles for wasm32, so clippy runs on
that target too.
Vercel's image has Node but no Rust. The install script puts rustup (with
the channel and wasm32 target from rust-toolchain.toml), wasm-pack and pnpm
in place; the build script runs wasm-pack, tsc and vite. Output is
web/dist. Nothing compiled is committed.
Rewrite the README for the app that is now deployed and give the web
package a README of its own.
@mahim37
mahim37 changed the base branch from core-sim to main September 20, 2026 08:24
@mahim37
mahim37 merged commit ab6ed87 into main Sep 20, 2026
13 of 14 checks passed

This branch had an error being deployed

1 failed deployment
Preview — 296850bc Deployed Sep 6, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant