---
phase: 05-live-show-execution
plan: 04
type: execute
wave: 1
depends_on: []
files_modified:
- lightsync/frontend/app.js
autonomous: true
gap_closure: true
requirements:
- SHW-03
- UI-03
- UI-04
must_haves:
truths:
- "Mouse play button triggers server is_playing=True via WebSocket play message"
- "Mouse pause button triggers server is_playing=False via WebSocket pause message"
- "UDP cues fire regardless of whether playback was started by mouse or keyboard"
artifacts:
- path: "lightsync/frontend/app.js"
provides: "WebSocket play/pause messages from audio element event listeners"
contains: "client.send.*type.*play"
key_links:
- from: "lightsync/frontend/app.js wireAudio() audio play listener"
to: "lightsync/api/ws.py is_playing"
via: "client.send({ type: 'play' })"
pattern: "audio\\.addEventListener\\('play'.*client\\.send"
- from: "lightsync/frontend/app.js wireAudio() audio pause listener"
to: "lightsync/api/ws.py is_playing"
via: "client.send({ type: 'pause' })"
pattern: "audio\\.addEventListener\\('pause'.*client\\.send"
---
Fix mouse-based playback not firing UDP cues.
Purpose: The transport bar play button calls audio.play()/pause() but never sends WebSocket play/pause messages to the server. The server's is_playing flag stays False, so the cue scheduler never fires UDP commands when using the mouse. The keyboard Space shortcut works correctly because wireKeyboard() sends the WS messages. This plan adds the same WS messages to wireAudio()'s audio element event listeners.
Output: Updated app.js where both mouse and keyboard playback trigger server-side cue scheduling.
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/05-live-show-execution/05-VERIFICATION.md
Task 1: Add WebSocket play/pause messages to wireAudio() event listeners
lightsync/frontend/app.js
lightsync/frontend/app.js
In `wireAudio()`, modify the two audio element event listeners (currently at lines 135-136) to also send WebSocket messages to the server.
Replace line 135:
```javascript
audio.addEventListener('play', () => { btn.textContent = '⏸'; startTicks(); });
```
With:
```javascript
audio.addEventListener('play', () => { btn.textContent = '⏸'; startTicks(); client.send({ type: 'play', position: audio.currentTime }); });
```
Replace line 136:
```javascript
audio.addEventListener('pause', () => { btn.textContent = '▶'; stopTicks(); });
```
With:
```javascript
audio.addEventListener('pause', () => { btn.textContent = '▶'; stopTicks(); client.send({ type: 'pause', position: audio.currentTime }); });
```
These listeners fire regardless of whether play/pause was triggered by button click, keyboard shortcut, or programmatic call. The keyboard Space handler in wireKeyboard() also sends play/pause — this is harmless because the server's play/pause handlers are idempotent (they just set is_playing to True/False).
Do NOT modify wireKeyboard() — it already works correctly.
Do NOT change any other part of wireAudio().
cd /home/claude/led2 && grep -n "client.send.*type.*play" lightsync/frontend/app.js | head -10 && grep -n "client.send.*type.*pause" lightsync/frontend/app.js | head -10
- grep "audio.addEventListener('play'" lightsync/frontend/app.js returns a line containing "client.send"
- grep "audio.addEventListener('pause'" lightsync/frontend/app.js returns a line containing "client.send"
- grep -c "client.send.*type.*'play'" lightsync/frontend/app.js returns at least 2 (one in wireAudio, one in wireKeyboard)
- grep -c "client.send.*type.*'pause'" lightsync/frontend/app.js returns at least 2 (one in wireAudio, one in wireKeyboard)
- The wireKeyboard() function is unchanged (still contains its own client.send calls at lines ~193 and ~196)
Both audio element event listeners ('play' and 'pause') in wireAudio() send WebSocket messages with type and position to the server. Mouse-based playback now sets server is_playing=True, enabling the cue scheduler to fire UDP commands.
1. `grep -A1 "addEventListener('play'" lightsync/frontend/app.js` — must show client.send with type 'play'
2. `grep -A1 "addEventListener('pause'" lightsync/frontend/app.js` — must show client.send with type 'pause'
3. No syntax errors: `node --check lightsync/frontend/app.js` (ES module parse check) or manual review of balanced braces
- Clicking the transport bar play button sends { type: 'play', position: N } to the server via WebSocket
- Clicking the transport bar pause button sends { type: 'pause', position: N } to the server via WebSocket
- The server's is_playing flag becomes True on mouse play, enabling the cue scheduler to fire UDP animation commands
- All existing keyboard shortcuts continue to work unchanged