SH-054: the shooter runs in the browser (WASM + WebGPU)#38
Conversation
perry compile --target web + the engine's bloom_web.wasm, same two-module architecture jump ships. tools/build-web.sh assembles dist/web (engine pkg, spliced HTML, assets + prefetch manifest, vendored JoltPhysics.js); tools/web-check.ts and web-play-check.ts drive headless Edge for boot/gameplay verification. Game changes, all guarded so native/iOS behave exactly as before: - The main loop is runGame(gameFrame) now. On native runGame IS the old blocking while loop (verified: COMBATSHOT runs identical numbers and exits at frame 260); on web the JS glue drives the same callback from requestAnimationFrame, because a browser cannot host a blocking loop. Harness/quit exits that used to `break` call closeWindow(). - WEB platform const (getPlatform() === 7) in input.ts. Guards: process.argv (no argv in a page), the boot splash/outro spin loops (synchronous frames never composite in a browser), settings persistence stays as-is (the glue maps writeFile to localStorage). - Web render profile: SSGI/SSR off, SSAO/shadows/bloom/TAA on, renderScale 0.65 - near the mobile profile until WebGPU perf is measured properly. - stageModels -> stageModelsSync on web: Perry's parallelMap workers are separate WASM instances with no engine FFI, so worker-side staging reaches nothing. - world-runtime computes its own WEB const: its module body runs at import time, and reading input.ts's export that early is an init-order ReferenceError (SH-025 lesson). - terrain.wgsl tri_normal samples with explicit gradients (textureSampleGrad; dpdx/dpdy hoisted to uniform control flow) - browser WGSL uniformity analysis rejects textureSample inside the layer-select branch. Identical mip math, no visual change. Requires the EN-063 engine branch (web FFI parity, scene-inputs bind-group fold, glue/bridge fixes) and the perry wasm-codegen fixes (new Array(n), namespace imports, re-export chains, ToInt32 bitwise, per-consumer function resolution).
|
Warning Review limit reached
Next review available in: 22 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (19)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Two causes: the synth wind was a FIXED bandpass x volume LFO (a static spectrum is what the ear calls white noise), and SH-052 had dropped the authored 40m window so the engine's long inverse-model tail spread that hiss over the whole arena. - gen-sfx wind v2: spectrum rides the gust (bough murmur in lulls -> leaf sizzle in gusts, brightness ~ gust^1.8, measured r=0.95), leaf flutter grains only in strong gusts, slow seeded wander, 24s loop. Seam verified (delta 0.23 vs body max 0.53), no clipping. - updateWindAmbience: authored window restored on top of engine rolloff (full <27m, silent >45m). Engine keeps pan/absorption/close-range. Skitter/crawl regenerated (shared seed stream, same contract as before). Claude-Session: https://claude.ai/code/session_015wJLdkwm71jQCAqypaFeNE
Browser WGSL uniformity analysis (Tint enforces what naga waves through) rejects implicit-derivative sampling inside tri_normal's per-fragment layer-select branch. Take the world-position gradients in uniform control flow and sample with textureSampleGrad — identical mip math, so no visual change on any platform. @invariant on the material clip positions for the same reason the engine's scene shader carries it: a position must compile identically in every pipeline built from the shader.
…the 3D Wind: two rebuilds could not make a spectral-only always-on synth bed stop reading as white noise, so it is LEFT OUT - sfxWind=NO_SOUND, every wind path early-returns as for a missing file. System stays live and dormant; one line re-enables it when a real canopy recording lands (ASSET-TODO A3). River/footsteps/creatures/reverb untouched. --audiotest orbit|flyby|river: a deterministic way to HEAR the spatialisation instead of inferring it from combat. - orbit: a broadband voice circles the listener at 6m (pan + rear cue) - flyby: it flies through you at ~14 m/s (doppler + front/back) - river: free-walk, river-only, dist-to-bank on the HUD Waves suppressed in all modes; orbit/flyby silence the river so the probe is the single source; river mode skips the probe. Top-centre HUD readout. Dormant unless --audiotest is passed. Verified: voice live, orbit angle advancing, all modes + normal path boot clean. Claude-Session: https://claude.ai/code/session_015wJLdkwm71jQCAqypaFeNE
feat: SH-052 - the living soundscape: river, wind loops, creature locomotion
./tools/build-web.sh→dist/web→ the full game in a WebGPU browser: menus, pointer-locked third-person combat, physics, water, forest, HUD — ~59 FPS in headless Edge, 0 console errors.Stacked on #37 (base =
feat/sh-052-living-soundscape); retarget tomainafter that merges. Requires engine PR Bloom-Engine/engine#118 and the Perry wasm-codegen fixes (local branchfix/wasm-new-array-sized— six codegen divergences found by this port).Game changes (native/iOS unchanged — verified)
runGame(gameFrame): on native,runGameIS the old blockingwhileloop — a COMBATSHOT regression run produces identical combat numbers and exits cleanly at frame 260 through thecloseWindow()calls that replaced barebreaks. On web the glue drives the same callback from rAF.WEBplatform const guards:process.argv, the boot splash/outro spin loops (synchronous frames never composite in a browser), fixed web render profile (SSGI/SSR off, scale 0.65).stageModelsSyncon web: Perry'sparallelMapworkers are separate wasm instances with no engine FFI.terrain.wgsl:tri_normalsamples with explicit gradients — browser WGSL uniformity analysis rejects implicit-derivative sampling in the layer-select branch. Identical mip math everywhere.@invarianton material clip positions.tools/build-web.sh(wasm-pack + perry --target web + splice + asset manifest for the glue's prefetch cache),tools/web-check.ts(headless-Edge boot check),tools/web-play-check.ts(drives PLAY, walks, fires),tools/web-anim-check.ts(walk-cycle capture — a skinned model on the wrong joint offset holds one pose forever).Fixed since first review
The three the player noticed were all engine-side (see #118): the scene was ~a stop dark (non-sRGB canvas + a tonemapper that relies on hardware sRGB encode), trees rendered as torn opaque sheets (
draw_model_rotatedstill on the pre-round-5 immediate path — no alpha cutout), and characters animated wrongly (draw_modelnever routed skinned models to the skinned cached draw, so every primitive read joint offset 0 — another model's matrices). A Perry bug compounded the trees:(a|r|g|b) >>> 0returned the signed value, so every model tint crossed the FFI negative, Rust's saturatingas u32floored it to 0, and the canopies discarded themselves.Known follow-ups
level.txtto localStorage but needs a page reload on web.