diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md
index f1f23ac..5075d8a 100644
--- a/.planning/ROADMAP.md
+++ b/.planning/ROADMAP.md
@@ -106,12 +106,13 @@ Plans:
3. The live preview panel shows a simulated LED strip visualization in the browser canvas, updated in sync with playback
4. All primary operations (play, pause, seek, undo, save) are accessible via keyboard shortcuts without touching the mouse
5. A show saved during one session loads completely — all blocks, devices, and analysis data — in a subsequent session
-**Plans**: 3 plans
+**Plans**: 4 plans
Plans:
- [x] 05-01-PLAN.md — Cue scheduler: server-side position-driven scheduling, 60ms look-ahead, seek-safe fired_ids reset
- [x] 05-02-PLAN.md — Live preview panel + show selector: DOM device strips, preview_update handler, show load into timeline
- [x] 05-03-PLAN.md — Keyboard shortcuts: Space, arrows, Ctrl+Z/Y/S for mouse-free operation
+- [ ] 05-04-PLAN.md — Gap closure: wire WebSocket play/pause messages from mouse transport button
**UI hint**: yes
### Phase 6: AI Sync
diff --git a/.planning/phases/05-live-show-execution/05-04-PLAN.md b/.planning/phases/05-live-show-execution/05-04-PLAN.md
new file mode 100644
index 0000000..8154c18
--- /dev/null
+++ b/.planning/phases/05-live-show-execution/05-04-PLAN.md
@@ -0,0 +1,127 @@
+---
+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
+
+
+