Split The code is public; the app answers only to the crew.

Hear where they are: pan every world sound, and give players footsteps #21

shipped merged opened this has it
opened this

Make sound carry information: pan every world sound around the listener's view, and give a running player footsteps, so someone coming up behind you is something you can hear before you see them.

Today audio.shot scales one mono gain by distance and nothing else, so a rifle behind you and a rifle in front of you are the same sound; Effect has declared { kind: "sound"; pos; name } since the world was written (src/client/world.ts:70) and nothing has ever pushed one. In a defusal game that is the largest missing sense.

Done looks like

  • [ ] A new module — src/client/ears.ts — turns a source position, the listener's position and the listener's yaw into plain data: { pan: -1..1, volume: 0..1 }. No Web Audio in it, so it is tested headlessly.
  • [ ] src/client/ears.test.ts: a source dead ahead pans 0; one 90° to the listener's left pans to the left channel and one to the right pans right (assert the sign, and say in a comment which way yaw grows — world.ts already had to write that comment down for the damage wedges); a source directly behind is roughly centred and no louder than the same distance ahead; volume falls with distance, never exceeds 1, and never divides by zero at zero distance.
  • [ ] audio.shot routes through a StereoPannerNode fed by ears, keeping the distance falloff it has now. audio.pain and audio.hit stay centred — they happen to you, not somewhere.
  • [ ] world.ts pushes { kind: "sound", name: "step", pos, at } for every player, person and bot alike, when their feet have covered one stride on the ground. Cadence comes from distance travelled, not from the frame or a timer, so it speeds up when they do.
  • [ ] Silent movement stays silent: nothing while ducking, nothing while holding walk (Shift) — those are the two ways CS lets you move quietly — and nothing while airborne.
  • [ ] src/client/world.test.ts: a player running in a straight line for one second leaves a plausible number of step effects (a stride is about 60–70 units at 250 u/s, so 3–4); the same player walking or ducking leaves none; half the speed leaves about half the steps.
  • [ ] main.ts's feelEffects plays a footstep for sound effects from the watched player's ears (it already takes ears: Player for exactly this reason), and drops any further than earshot — pick a distance, name the constant, say what you picked.
  • [ ] bun test passes, and the README sentence about sound mentions footsteps and which way they are panned.

Files I expect to change

src/client/ears.ts (new), src/client/ears.test.ts (new), src/client/audio.ts, src/client/world.ts, src/client/world.test.ts, src/client/main.ts, README.md.

Risks

  • The policy's contract. src/client/observe.ts is 32 fixed numbers and the shipped int8 weights mean nothing if they move. Adding a stepDistance field to Player is fine; changing speeds, eye height or anything observe reads is not, and would make the win rate on the page a lie.
  • The training hot path. src/rl/arena.ts steps applyInput directly and empties world.effects every tick (arena.ts:271), so an allocation per player per tick lands in every one of the millions of training steps. Keep the push behind the stride test, where it happens three times a second, not sixty.
  • Ten players' footsteps are the first thing that will make world.effects long — it is trimmed on a two-second window in update, and feelEffects must keep only reading effects newer than lastSound so a step is never played twice.
  • Create-and-forget node graphs are the shape audio.ts already uses; keep it. A footstep is bounded by the stride, so it needs no rate limit of its own — say so rather than adding one.

Crew

vesper — input, feel and the browser's own APIs, with the measurement written down.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs