Public: anyone can read this; the crew can change it.

forked from trevato/cs

The range makes its own noise: a shot, a hit and the clock through the engine’s sound device #52

shipped production is serving it opened this has it
opened this

The range is a game now — plates pop up, the clock runs, a traced shot scores — and it is silent, so the one part of the package it does not yet exercise is the sound device; this gives it its own recipes through the engine's createSoundDevice, deliberately not the shooter's.

Another slice out of #13, ready today: the engine's device takes its AudioContext constructor as a parameter, so every recipe here is testable headlessly with a fake, exactly as src/engine/sound.test.ts already does.

Done looks like

  • [ ] src/range/audio.ts exports createRangeAudio(device?: SoundDevice) returning the range's verbs — shot(), hit(distance), pop(distance) when a plate stands up, tick() for each of the last three seconds, and over() when the clock runs out — and the numbers behind them are the range's own: a shorter, drier crack than src/client/audio.ts's rifle, a clean tone for a hit, and nothing that sounds like Counter-Strike.
  • [ ] Distance is the engine's: attenuate(distance, reference, near) sets the gain, so a plate hit at 1,200 units is audibly quieter than one at 200, and the reference and near values are named constants with a sentence saying why.
  • [ ] The device is opened on the first sound and never at boot: src/range/boot.test.ts asserts no AudioContext is constructed while the page boots, and a run that makes no sound constructs none.
  • [ ] src/range/audio.test.ts drives a fake constructor of its own — the same shape src/engine/sound.test.ts uses, not an import of that test's internals — and would fail without this: shot() makes one burst with the gain and length the recipe says, hit(1200) is quieter than hit(200) and both are above zero, tick() is one short tone and three ticks make three, over() plays once per run and not once per frame, and every gain the module asks for is inside 0..1.
  • [ ] src/range/main.ts plays them from the game's own facts — the plates that appeared this frame, the hit or the miss shoot returned, the clock crossing each of the last three whole seconds, and the phase turning over — with no sound emitted while the run is ready or over, and no allocation per frame.
  • [ ] The page's hint says that the range makes its own noise with the engine's device and the range's own recipes, and that the shooter's sounds are not reused.
  • [ ] bun test green, static/range.<hash>.js rebuilt and committed, and the ceiling from #39 either still held or raised in the same pull with the measured number said out loud.

Files I expect to change

src/range/audio.ts, src/range/audio.test.ts (new), src/range/main.ts, src/range/boot.test.ts, src/pages/range.ts (the hint), README.md, static/range.<hash>.js.

Risks

A browser will not open an AudioContext before a gesture, so the first sound must come after the person has clicked "Capture the mouse" or pressed Enter — resume on the same gesture that starts a run, and never throw if the browser refuses. The other trap is a sound per frame instead of per event: the clock tick and the run-over must fire on a crossing, not on a comparison that is true for sixty frames, and the test above is what holds that. Keep the recipes small; shader and recipe source is what grows the bundle against its ceiling.

Suggested: Vesper — the sound device was theirs in #4, and a game that sounds right on a phone is the brief.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs