Three review findings on the per-panel camera work:
- highway_3d: _bgPanelKey() resolved splitscreen via window.feedBackSplitscreen
only, while _freeCamFor() uses the feedBackSplitscreen||slopsmithSplitscreen
alias it claims to "mirror". If the rename lands, per-panel background settings
would silently stop being per-panel while the camera stayed per-panel. Resolve
the alias the same way in _bgPanelKey.
- drum_highway_3d: applyCamera()'s "before first positionCamera()" guard tested
`_camBaseH == null`, but _camBaseH/_camBaseD were initialized to 0, so the guard
never fired (and could apply a base-0 pose for a frame). Initialize to null.
- keys + drum: the PR claimed the Camera Director resolver was unit-checked, but
nothing exercised it. Extract the resolver into pure, exported helpers
(_resolveFreeCam + _ssApi), delegate the per-instance _freeCamFor to them, and
add tests/camera_bridge.test.js covering per-panel select, global fallback,
null-when-absent, throw-safety, and the slopsmith-alias resolution. Drum 15→21,
keys 50→56, all pass; behavior unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Kris Anderson <topkoa@gmail.com>
- BG_STYLES port (off/particles/lights/geometric) into a renderOrder -1
group; _bgGetAnalyser/_bgReadBands (stems-first, one-shot #audio
fallback, permanent-failure latch, 5ms bands cache); Ambience
intensity slider + Audio-reactive toggle; remounts on style/intensity/
palette change and across kit-change scene rebuilds
- Score-FX overlay canvas (guitar drawScoreFx adapted to internal
scoring): +1 pops at the struck lane, ring pulse every 10-combo,
milestone bursts at 25/50/100, red wash on 3+ streak break; pooled,
cleared when idle, removed in teardown
- butterchurn/image/video deliberately out of scope
- Tests: style id validation + FX defaults (15 total)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
- Pooled spark bursts (PORTED highway_3d, pool 160) at the struck lane,
timing-colored via _timingHex/_classifyTiming (±50ms window, inner 40%
= on-time); streak-scaled counts (streakFx)
- Lane flashes resurrected as pooled additive gauss-tex quads at the hit
line (timing-colored; red for wrong hits); kick = full-width quad
- Kick pulse: camera dip + amber floor wash, exp decay, hitFx-scaled
- Approach highlight: lane stripes brighten toward their next note
- Open hi-hat: hh_open renders a warm ring around the gem (closes TODO);
orthogonal to accent/ghost/flam variants
- Settings: Hit sparks / Timing colours / Streak feedback toggles +
Hit feedback intensity slider (drum_h3d_bg_*, live-applying)
- All FX resources pooled/shared, disposed in BOTH teardown paths
- Tests: _classifyTiming boundaries + FX defaults (10 total)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
- Consume host adaptive-quality bundle.renderScale; fold into DPR like
highway_3d (splitscreen proxy: >1 instance caps baseDPR at 1.25)
- Bloom: port _bloomEnsure/_bloomDispose (UnrealBloomPass 0.65/0.5/0.82,
MSAA HalfFloat target, ACES<->None tone-mapping switch); rebuilt across
the kit-change renderer recreation; direct render is the degrade path
- FX settings scaffold: FX_DEFAULTS + readFxSettings + drumH3dSetFx
(drum_h3d_bg_* keys), Graphics section in settings.html (bloom toggle,
default ON, live-applies)
- _applyLaneFlashes dead-code comment updated (visual consumer lands in
the hit-FX PR)
- __test export + tests/data_layer.test.js (vm harness, 8 tests) —
picked up by the CI glob from the bundling PR; README refreshed
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>