Public: anyone can read this; the crew can change it.
forked from trevato/cs · proposes · 4 behind
The range makes its own noise: a shot, a hit and the clock through the engine’s sound device #82
Check · Review · Land · Live done — Landed and live in production.
The range has a voice now, and it is the engine's device with the range's own recipes: `src/range/audio.ts` is five verbs — `shot()`, `hit(distance)`, `pop(distance)`, `tick()` and `over()` — over `createSoundDevice`, each a named constant a test can read. The crack is a 2,400 Hz burst for 70 ms with no thump under it, against `src/client/audio.ts`'s rifle at 1,500 Hz ringing for 240 ms with one;…
shipped production is serving it deploy ae07c2d
Sign in to join this conversation.
new above
offline
The range has a voice now, and it is the engine's device with the range's own recipes:
src/range/audio.tsis five verbs —shot(),hit(distance),pop(distance),tick()andover()— overcreateSoundDevice, each a named constant a test can read. The crack is a 2,400 Hz burst for 70 ms with no thump under it, againstsrc/client/audio.ts's rifle at 1,500 Hz ringing for 240 ms with one; the hit is a clean sine, a plate standing up is a knock that drops, the tick is short and the clock running out is that same note falling away. Distance is the engine'sattenuateon this room's numbers —EARSHOT320 andCLOSE160, each with a sentence saying why — so a plate hit at 1,200 units is a quarter the gain of one at 200, and every gain the module asks for is inside 0..1.main.tsplays them off crossings in the game's own facts, never off a comparison that stays true: a plate whose id the frame before had not reached popped, theShotthatshoothanded back cracks and (when it found a plate) hits, the clock passing 3, 2 and 1 ticks once each, andwasLive && phase === "over"plays the last tone. Areadyoroverrun is silent, a trigger pulled outside a run is refused and therefore silent, and a frame with nothing to say allocates nothing. The device is opened on the gesture that starts a run —audio.resume()on the Enter edge, wrapped so a browser that refuses never throws — and never at boot.Worth looking at:
src/range/audio.test.tsdrives a fakeAudioContextconstructor of its own (the shapesrc/engine/sound.test.tsuses, written again rather than imported, because it reads nodes as well as calls);dom.tsnow hands the stub page anAudioContextthat counts constructors and voices, andboot.test.tsreads those voices by name — zero constructors at boot and for a page nobody plays, one for a whole run, one crack for a held trigger, and exactly three ticks plus one run-over tone from a thirty second run of 190 frames.bun testis green (474 pass). The range's ceiling is raised from 26,700/11,600 to 29,400/12,700: sound cost 2,444 raw and 956 gzipped bytes and the bundle measures 26,658 raw / 11,456 gzipped today, which would have fitted the old ceiling by 42 bytes — not a ceiling anybody is holding. No mute control: the page has no button for one and nothing here stores a preference, so it stayed out.In the preview, open
/range, click Capture the mouse, press Enter and listen: plates knock as they stand, the crack and the tone separate a hit from a miss, and the last three seconds count themselves out.🤖 Generated with Claude Code
Closes #52