feat(drum_highway_3d): audio-reactive ambience + score FX overlay (D4) (#708)

- 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>
This commit is contained in:
Byron Gamatos
2026-07-02 09:01:56 +02:00
committed by GitHub
co-authored by Claude Fable 5
parent 4214ef365e
commit d409d55615
5 changed files with 621 additions and 0 deletions
@@ -148,3 +148,17 @@ test('FX defaults: theme-PR controls ship enabled at stock-neutral values', () =
assert.equal(FX_DEFAULTS.glow, 0.5); // 0.5 = 1.0x multiplier (stock)
assert.equal(FX_DEFAULTS.vibrancy, 0.85); // ≈ the stock 0.32 stripe base
});
test('bg styles: validated id set, particles default, no out-of-scope styles', () => {
const { BG_STYLE_IDS, readBgStyleSetting } = load().__test;
// Host-realm copy — the vm array's foreign prototype trips deepEqual.
assert.deepEqual([...BG_STYLE_IDS], ['off', 'particles', 'lights', 'geometric']);
assert.equal(readBgStyleSetting(), 'particles'); // no localStorage in the vm
});
test('FX defaults: ambience + score FX ship enabled', () => {
const { FX_DEFAULTS } = load().__test;
assert.equal(FX_DEFAULTS.scoreFx, true);
assert.equal(FX_DEFAULTS.bgIntensity, 0.5);
assert.equal(FX_DEFAULTS.bgReactive, true);
});