From c049cddb6d2591f179215916abafce3ad231107b Mon Sep 17 00:00:00 2001 From: cottongin Date: Fri, 20 Mar 2026 12:43:31 -0400 Subject: [PATCH] Add design doc for overlay manager and player list Event-driven state machine to coordinate room code display, audio, and a new player slot list. Fixes audio restart bug by centralizing lifecycle management. Adds new shard-based WebSocket event handling. Made-with: Cursor --- ...3-20-overlay-manager-player-list-design.md | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 docs/plans/2026-03-20-overlay-manager-player-list-design.md diff --git a/docs/plans/2026-03-20-overlay-manager-player-list-design.md b/docs/plans/2026-03-20-overlay-manager-player-list-design.md new file mode 100644 index 0000000..b291a12 --- /dev/null +++ b/docs/plans/2026-03-20-overlay-manager-player-list-design.md @@ -0,0 +1,154 @@ +# Overlay Manager & Player List Design + +**Date:** 2026-03-20 +**Status:** Approved + +## Problem + +The OBS overlay (`optimized-controls.html`) has two issues after the upstream Game Picker API migrated from Puppeteer to an ecast shard monitor: + +1. **Audio doesn't restart on new room codes.** The overlay updates the room code text when `game.added` fires, but audio playback fails to reinitialize because `hideDisplay()` (triggered by `game.started` or `audience.joined`) leaves the audio element in a state that `startAnimation()` doesn't fully recover from. There is no centralized lifecycle management — show/hide logic is scattered across event handlers. + +2. **No player list.** The new shard-based API pushes real-time player names and join events (`room.connected`, `lobby.player-joined`), but the overlay doesn't consume them. Players joining the lobby should be visible to viewers. + +Additionally, several new WebSocket events (`room.connected`, `lobby.player-joined`, `lobby.updated`, `game.ended`, `room.disconnected`) are not handled, and the removed `audience.joined` event is still referenced. + +## Design Decisions + +- **Event-driven state machine** over reactive flags or per-component lifecycle management. A central `OverlayManager` coordinates all components through explicit state transitions, preventing the ad-hoc state bugs that cause the current audio issue. +- **ES module split** (no build step) over single-file or bundled architecture. Keeps OBS browser source simplicity while improving maintainability. +- **WebSocket-only player data** — no REST polling fallback for player lists. +- **Overlay visible only during lobby state** — room code, audio, and player list all share the same lifecycle. + +## Architecture + +### State Machine + +``` +idle → lobby → playing → ended → idle + ↑ | + └─────────────────────────┘ + (new game.added) + +Any state → disconnected → idle (on reconnect, no active lobby) + → lobby (on reconnect, active lobby) +``` + +| State | Entry Trigger | Visible Components | +|-------|--------------|-------------------| +| `idle` | Initial load, `session.ended`, `game.ended`, `room.disconnected` | None | +| `lobby` | `game.added`, `room.connected` (lobby state) | Room code + audio + player list | +| `playing` | `game.started` | None | +| `ended` | `game.ended` | None (transitions to `idle` after brief delay) | +| `disconnected` | WebSocket close/error | None (reconnect logic runs) | + +Key transition: `game.added` while already in `lobby` triggers a **full reset** — deactivate all components, update context, reactivate. This fixes the audio restart bug by design. + +### Component Interface + +Every component implements: + +- `activate(context)` — enter active state with room/game/player context +- `deactivate()` — fully clean up (stop audio, clear timers, hide elements) +- `update(context)` — handle in-state updates (new player joined, etc.) +- `getStatus()` — return current status for the debug panel + +### File Layout + +``` +OBS-stuff/ +├── optimized-controls.html # DOM + CSS + bootstrap script +├── js/ +│ ├── state-manager.js # OverlayManager: state machine, component registry +│ ├── websocket-client.js # Auth, connect, reconnect, event routing +│ ├── room-code-display.js # Room code animation component +│ ├── audio-controller.js # Audio playback lifecycle +│ ├── player-list.js # Player slot list component +│ └── controls.js # Settings panel + debug dashboard +``` + +Loaded via `