7.1 KiB
7.1 KiB
Phase 5: Live Show Execution - Context
Gathered: 2026-04-07 Status: Ready for planning
## Phase BoundaryA complete show runs end-to-end: audio plays, animation blocks fire UDP commands to LED devices at correct timestamps, a live preview panel shows per-device active animations in the browser, and shows persist across sessions. Keyboard shortcuts make all primary operations mouse-free. No AI sync (Phase 6), no firmware (Phase 7).
## Implementation DecisionsCue Scheduler Architecture
- D-01: Server-side scheduling — the server fires UDP commands, not the browser. On each WebSocket
tick, the server scans the current show's cue list and dispatchesUDPSender.send()for any block due to fire. - D-02: 60ms look-ahead window — if a block's
timestampis within 60ms ahead of the current tick position, fire it immediately. This prevents missed cues at 10Hz tick rate (max ~100ms tick gap). Blocks fire slightly early, never late. - D-03: Seek-safe reset — on
seekevents, the server resets the "already fired" cue pointer so no cues are double-fired or skipped after a seek. - D-04: The server needs the current show's cue list available per-connection. Wire this up by loading the active show from
show_storein the WebSocket handler when aloadmessage arrives (browser already sends this when audio loads).
Live Preview Panel
- D-05: Animation color + type per device — show the active animation type and its color as a simple strip per device. No per-LED frame simulation; just which animation is active and in what color. Turns dark when no block is active on a device.
- D-06: Panel placement: below the timeline canvas, above the transport bar — always visible during playback. Layout: TIMELINE → LIVE PREVIEW → TRANSPORT BAR. Matches the Phase 4 inspector strip placement approach.
- D-07: Preview updates are driven by the same WebSocket
tickloop — when the server fires a cue, it also broadcasts apreview_updatemessage so the browser canvas reflects the new state.
Claude's Discretion
- Keyboard shortcuts (UI-03): Standard DAW conventions: Space = play/pause, Left/Right arrow = seek ±5s, Ctrl+Z = undo, Ctrl+Shift+Z or Ctrl+Y = redo, Ctrl+S = explicit save. Planner decides exact key bindings but these are safe defaults.
- Show save/load UX: All block mutations already auto-save via CRUD API (Phase 4). Add Ctrl+S as an explicit "save now" shortcut that calls the existing save endpoint. Show loading: use the existing show selector dropdown + a "load" button. No need to redesign.
- Preview panel height: Compact — 40-60px total. One row per device, enough for a labeled color strip. Collapsible if many devices.
<canonical_refs>
Canonical References
Downstream agents MUST read these before planning or implementing.
Requirements
.planning/REQUIREMENTS.md— SHW-03 (live show execution), UI-03 (keyboard shortcuts), UI-04 (live preview panel).planning/ROADMAP.md§Phase 5 — Success criteria (5 items), plan breakdown (05-01 through 05-03)
Existing WebSocket protocol
lightsync/api/ws.py— Tick/beat WebSocket handler; cue scheduler extends this handler. Already handlestick,play,pause,seek,loadmessage types.
UDP dispatch
lightsync/protocol/animation_cmd.py—encode_animation_cmd(animation, params)+ANIMATION_IDSregistrylightsync/protocol/udp_sender.py—UDPSender.send(payload, ip, port)— already wired inmain.py
Data models
lightsync/models/show.py—ShowModel,TrackModel,CueModel(fields:id,timestamp,duration,animation,params)lightsync/models/device.py—DeviceConfig(fields:id,name,strip_type,led_count,target_ip,target_port)
Show persistence
lightsync/api/shows.py— Existing save/load REST API (GET/POST /api/shows,GET /api/shows/{id})lightsync/api/timeline.py— Block CRUD endpoints (all mutations already auto-save viashow_store.save())
Existing frontend
lightsync/frontend/app.js— WebSocket client, 10Hz tick loop, show/device state, existing event wiringlightsync/frontend/timeline/timeline.js—TimelineCanvas,this.tracks,this.showId— preview panel reads same track datalightsync/frontend/style.css— CSS variables; preview strip must match terminal aesthetic
Prior context
.planning/phases/01-foundation/01-CONTEXT.md— Visual aesthetic decisions (flat, sharp, cyan/teal, no rounded corners, no shadows).planning/phases/04-timeline-editor/04-CONTEXT.md— D-01/D-03: inspector strip placement pattern; preview panel follows same layout slot logic
</canonical_refs>
<code_context>
Existing Code Insights
Reusable Assets
ws.pyConnectionManager.broadcast()— already wired for multi-client broadcast;preview_updatemessages can use thisUDPSender(wired inmain.py) — available asapp.state.udp_senderor module-level ref; Phase 5 callssend()from the tick handlershow_store.load(show_id)— async, used inshows.pyandtimeline.py; ws.py can call this onloadmessagelightsync/frontend/timeline/timeline.jsthis.tracks— the live track data (including blocks) is already in the browser; preview can readtimeline.tracksdirectly to know which blocks are active ataudio.currentTime
Established Patterns
- WebSocket message protocol:
{"type": "...", ...}— extend with{"type": "preview_update", "device_id": "...", "animation": "chase", "color": [0, 255, 180]} - Server-side per-connection state pattern already used in
ws.py(current_file,beats,last_beat_reported) - CSS:
--accent(cyan),--bg(near-black),--border(dimmed cyan); panel borders1px solid var(--border) - Panel strip pattern from Phase 4:
<div id="block-inspector">horizontal row — preview strip follows same structure
Integration Points
ws.pywebsocket_endpoint function — add cue scheduler loop inside thetickbranch; needsshowloaded per-connectionmain.py—app.state.udp_senderalready initialized; access from ws.py viawebsocket.app.state.udp_senderindex.htmllayout — preview panel slot between.timeline-containerand.transport-barapp.jsWebSocketonmessagehandler — addpreview_updatecase to update the preview canvas
</code_context>
## Specific Ideas- Terminal aesthetic carries forward: preview strips should look like terminal status bars — flat rectangles, color fill, device name label in monospace uppercase, no rounded corners.
- The "lights dark" / "no active block" state should show the device label with a dim inactive indicator — consistent with the rest of the UI's use of
--text-dim.
- Per-LED frame simulation in the preview (see what the chase animation actually looks like moving) — v2 or Phase 6+
- Preview panel as a standalone full-screen mode — out of scope
- Keyboard shortcut customization UI — out of scope for v1
Phase: 05-live-show-execution Context gathered: 2026-04-07