LANE-JUICE: session block + contract change request for door:phoneTheatre
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
2eb3c94ccd
commit
f5e1151c6e
@ -95,3 +95,74 @@ walking under an ID card claiming 18+ — that's the whole game in one image.
|
|||||||
DOOR first, JUICE second, FLOOR third.
|
DOOR first, JUICE second, FLOOR third.
|
||||||
3. Every session ends with the gate + a SESSION block here + a push. Fable
|
3. Every session ends with the gate + a SESSION block here + a push. Fable
|
||||||
reviews on request ("review the lanes").
|
reviews on request ("review the lanes").
|
||||||
|
|
||||||
|
---
|
||||||
|
### SESSION — LANE-JUICE — 2026-07-19 18:55
|
||||||
|
**Branch/commits:** lane/juice @ c262ac8..2eb3c94
|
||||||
|
**Done:**
|
||||||
|
- `audio/TechnoEngine.ts` — synthesized 128 BPM track (kick / off-beat hats /
|
||||||
|
2-bar 16th bassline / 8-bar pad), look-ahead scheduler (25ms timer, 100ms
|
||||||
|
horizon) as the beat authority. Emits `beat:tick {beatIndex, audioTimeMs}`
|
||||||
|
with the **scheduled** time, plus `beat:bar` on downbeats. Drop-in replacement
|
||||||
|
for `core/StubBeatClock`. Music → lowpass → master; SFX bypass the filter.
|
||||||
|
- `audio/Sfx.ts` — 10 procedural one-shots + `startRain`/`stopRain`. One shared
|
||||||
|
noise buffer; voices disconnect on `ended`.
|
||||||
|
- `audio/scheduling.ts` + `audio/filterCurve.ts` — pure, node-testable beat grid
|
||||||
|
and cutoff-ramp math (the WebAudio classes are thin wrappers over these).
|
||||||
|
- `ui/` — `Stamp`, `Phone`, `DialogueBox`, `Clicker`, `MeterHud`, plus
|
||||||
|
`style.ts` (chunky widget kit) and `typo.ts` (deterministic drunk typos).
|
||||||
|
- `ui/JuiceDemoScene.ts` — exercises every widget and SFX; log-mapped cutoff
|
||||||
|
slider, DOOR/FLOOR toggle routed through the bus, beat pad that flashes when
|
||||||
|
`ctx.currentTime` reaches the scheduled time (not on event receipt).
|
||||||
|
**How to see it:** `npm run dev` → http://localhost:5199/#juice (or press **J**;
|
||||||
|
**P** returns to the parade). Click once to unlock audio, then: drag MUFFLE to
|
||||||
|
sweep the filter by hand, hit STEP INSIDE for the door→floor sweep, mash the
|
||||||
|
SOUNDS column, stamp someone with NOT TONIGHT.
|
||||||
|
**Tests:** 111 passing / 0 failing (was 49 at Phase 0; +62 this lane).
|
||||||
|
**Verified by ear:** NOTHING. No audio output available in this environment.
|
||||||
|
Everything below was logic-checked in a real browser via the exposed engine:
|
||||||
|
- door→floor sweep ramps 250Hz→18kHz over 600ms, monotonic, exponential
|
||||||
|
(t=0.5 lands ~2.1kHz vs the ~9.1kHz a linear ramp gives).
|
||||||
|
- under a genuinely hidden/throttled tab: 52 consecutive beats, **zero**
|
||||||
|
scheduled in the past, 75–98ms lead, max 1 beat scheduled per timer wake.
|
||||||
|
**John/Fable still need to ear-check:** does the kick read as a kick, is the
|
||||||
|
bassline musical, is `typeTick` quiet enough at the real mix level (it fires per
|
||||||
|
character), and does the sweep *feel* like a door rather than a filter.
|
||||||
|
**Broke / known-wonky:**
|
||||||
|
- `src/main.ts` is edited (2 lines + a ~12-line demo route). **Outside LANE-JUICE
|
||||||
|
ownership** — see request 2 below. Without it the demo scene is unreachable
|
||||||
|
and Vite tree-shakes it out entirely.
|
||||||
|
- `TechnoEngine` has no test file — it needs a real `AudioContext` and the suite
|
||||||
|
runs in node. Its scheduling/filter math is tested via the pure modules, but
|
||||||
|
the `resync` arithmetic is only reachable through the class. Extracting
|
||||||
|
`resyncBeat(grid, nextBeat, nowMs)` into `scheduling.ts` would close that gap.
|
||||||
|
- `Phone` thread truncates an over-tall newest message rather than scrolling.
|
||||||
|
- `Stamp` decals are caller-owned now; the demo caps them at 5. The door scene
|
||||||
|
must destroy them (or pass `parent`) or they accumulate over a night.
|
||||||
|
**Contract change requests:**
|
||||||
|
1. **Add `door:phoneTheatre: { durationMs: number }` to `EventMap`.** Emitted by
|
||||||
|
`ui/Phone` when the player closes the phone, carrying how long they stared at
|
||||||
|
it while a queue waited — the door scene rewards it with hype. It is a door
|
||||||
|
event, not an audio one, so it is outside my namespace. **I did not edit
|
||||||
|
`data/types.ts`.** `ui/juiceEvents.ts` declaration-merges it into `EventMap`
|
||||||
|
instead: fully typed, zero edits to the frozen file, and folding it into the
|
||||||
|
real contract later is a copy-paste. Same file also adds `audio:unlocked` and
|
||||||
|
`beat:bar`, which ARE mine per LANE_JUICE.md but are declared together so the
|
||||||
|
whole juice surface reads in one place.
|
||||||
|
2. **Sign-off requested on the `src/main.ts` edit** (scene registration + J/#juice
|
||||||
|
route, marked `// TODO(integration)`). Happy to revert it if you would rather
|
||||||
|
wire the demo from the Phase 2 shell yourself.
|
||||||
|
**Questions for reviewer:**
|
||||||
|
1. `TechnoEngine.unlock()` anchors beat 0 at `currentTime + 60ms` so beat 0 is as
|
||||||
|
honest as every other beat. If a consumer assumes beat 0 lands exactly at
|
||||||
|
`audio:unlocked.atMs`, that is a 60ms discrepancy — is that acceptable, or
|
||||||
|
should `atMs` carry the anchored origin instead?
|
||||||
|
2. `MeterHud` counts heat strikes from `heat:strike` events it receives. On a
|
||||||
|
mid-run scene swap it would restart from 0. Should it take an initial count in
|
||||||
|
its options, or read `NightState.heatStrikes.length` at construction?
|
||||||
|
**Next session should:**
|
||||||
|
- LANE-DOOR: swap the local stamp for `ui/Stamp` (`// TODO(juice)`), and take
|
||||||
|
`MeterHud` in place of `HudStub`.
|
||||||
|
- Someone with speakers should ear-check the mix and tell me what to retune.
|
||||||
|
- Consider extracting `resyncBeat` into `scheduling.ts` so the throttled-tab
|
||||||
|
resync gets direct test coverage.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user