Driven by the live fresh-eyes finding: a naive racer finishes with 0% paint — the core mechanic is currently skippable. G makes paint route-guaranteed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
45 lines
2.6 KiB
Markdown
45 lines
2.6 KiB
Markdown
# Lane F — Audio (procedural, event-driven)
|
|
|
|
**Owns:** `src/audio/`, `demos/lane-f.html`, `src/demo/lane-f.ts`.
|
|
**Never touches:** contracts, world, game, main, package.json, other lanes' dirs.
|
|
No new deps, no audio asset files — **everything synthesized with WebAudio**
|
|
(oscillators, noise buffers, filters, envelopes). Comedy > realism.
|
|
|
|
## Mission
|
|
BLOBBO is silent. Sound is half the comedy of a wobble game. Build a tiny
|
|
procedural sound engine that reacts to the frozen wire events only.
|
|
|
|
## Deliverables (`src/audio/`)
|
|
1. `engine.ts` — `AudioEngine`: lazy `AudioContext` (create/resume ONLY after
|
|
the `ui:play` event or first user gesture — browsers block earlier), master
|
|
gain, mute toggle (persist in localStorage `blobbo:muted`), and a small
|
|
polyphony cap (~8 voices) so event storms don't clip.
|
|
2. `sounds.ts` — synthesized one-shots, all pitch-randomized ±10% per play:
|
|
- **squish** (blob:landed — gain and pitch scale with `impact`; big drops = big splat)
|
|
- **boing** (blob:jumped — springy sine sweep; also used by the gap launcher)
|
|
- **splat** (paint:splatted target:'blob' — wet noise burst; slightly
|
|
different pitch per colour, use PALETTE order for a pentatonic-ish mapping)
|
|
- **sparkle** (paint:request-cleanse — bubbly arpeggio)
|
|
- **clank+windup** (machine:signal — ratchet tick accelerating ~0.5s, matches the telegraph)
|
|
- **fanfare** (race:finished — dumb triumphant 4-note; extra flourish if new best)
|
|
- **tick** (race:started — single starter blip)
|
|
- optional ambient: very quiet lo-fi room tone / conveyor hum when near machines — only if cheap.
|
|
3. `install.ts` — `installAudio(world)`: subscribes to the events above on
|
|
`world.events`, returns `{ engine }`. Listens for `ui:play` to unlock.
|
|
A tiny 🔈/🔇 DOM button bottom-right toggles mute.
|
|
4. Demo `demos/lane-f.html` + `src/demo/lane-f.ts`: sound board — a button per
|
|
sound + sliders for the impact parameter. Verifies every sound in isolation.
|
|
|
|
## Acceptance
|
|
- `npm run build` passes. No AudioContext created before first gesture/`ui:play`
|
|
(verify: constructing the engine logs nothing and `ctx === null` until unlock).
|
|
- Every listed event produces a distinct, non-clipping sound; rapid splat storms
|
|
(10/s) stay clean via the voice cap.
|
|
- Mute persists across reloads.
|
|
- Sounds are SHORT (<400ms except fanfare) and mixed well below pain (master ~-12dB).
|
|
|
|
## Events you consume (never emit gameplay events)
|
|
`blob:landed {impact}`, `blob:jumped`, `paint:splatted {color,target}`,
|
|
`paint:request-cleanse`, `machine:signal {id}`, `race:started`,
|
|
`race:finished {time,best}`, `ui:play` (from Lane H's title screen).
|