Exit condition (Jake, sync): “a blog post + video about lessons from making a harness.”
blog post + video published → DONE → archive this node
MIDI Harness
Active project · play a couple chords, have an interesting discussion about music. Code lives in its own repo: ~/Documents/midi-harness.
The MIDI harness is an example of what I want it to be: a really simple system prompt and a hook that captures MIDI events from a JSON-L and then an HTML, so that we're basically just trying to give Claude the power to let me play a couple chords and then have an interesting discussion about music.
The loop
events.jsonl after injecting it — so "since last message" needs no offsets, no time windows, no second clock, no audio.
Listener — start / watch / stop
Runs ~/Documents/midi-harness/bin/listener (builds first if needed). Notes scroll below as you play. Pill polls every 5s, so it also sees listeners started from a terminal or the repo's SessionStart hook. Stop kills the listener no matter who started it. Local-viewer only.
(no output yet — start the listener, then play)
The full picture — diagram for the blog post
┌──────────────────────────┐
│ USER │
│ (plays + talks at the │
│ same time) │
└────┬────────────────┬────┘
hands on │ │ voice
MIDI keys │ │ (Wispr Flow → text)
▼ ▼
┌────────────────────┐ ┌──────────────────┐
│ listener (Go) │ │ Claude Code │
│ daemon, all session│ │ input box │
│ raw note on/off, │ └────────┬─────────┘
│ no cleanup │ │ user hits ⏎
└─────────┬──────────┘ ▼
│ append ┌─────────────────────────────┐
▼ │ hook (Go, UserPromptSubmit) │
┌──────────────────┐ │ │
│ events.jsonl │ │ ① read WHOLE file │
│ raw + noisy: │ │ ② delete file │
│ {"t":..., │───►│ ③ clean up (in memory): │
│ "type":"on", │ │ · cluster press→release │
│ "note":60} │ │ into chords │
│ one line per │ │ · collapse repeats → xN │
│ key event │ │ · note numbers → names │
└──────────────────┘ │ ④ emit <played> block │
▲ └──────────────┬──────────────┘
│ │
next note played │ e.g. 34 raw JSON lines in →
recreates the file │ 3 lines of chords out
(fresh turn boundary) ▼
┌──────────────────────────────┐
│ Claude │
│ prompt = user's text │
│ + <played> block │
│ CLAUDE.md ≈46 words: │
│ "piano practice partner" │
└──────────────┬───────────────┘
▼
talk about the music
(+ ad-hoc HTML finger
diagrams — not a skill,
Claude just writes one)
The 34→3 example: a 4-key C-major voicing struck & released 3× (24 on/off events) + a 4-key F voicing (8) + one G4 (2) = 34 JSON lines on disk → C3 G3 C4 E4 x3 / F2 F3 A#3 D4 / G4.
Recipe card
┌─ RECIPE: midi-harness ─────────────────────────────────┐ │ │ │ hardware │ │ · 1 MIDI keyboard (USB) │ │ │ │ off-the-shelf │ │ · Claude Code (the chat + hook runtime) │ │ · Wispr Flow (voice → text in the input box) │ │ │ │ written for this │ │ · 1 Go listener (~90 lines — append raw notes │ │ to events.jsonl, forever) │ │ · 1 Go hook (~160 lines — read, clean, │ │ inject <played>, delete) │ │ · 1 system prompt (CLAUDE.md, 46 words) │ │ │ │ no database · no server · no audio · no clocks │ └────────────────────────────────────────────────────────┘
Plus ~20 lines of glue: settings.json + two shell scripts that auto-start/stop the listener with the Claude session. A hook can be any language — Go is a latency choice (~1ms startup vs 30–170ms interpreter dead air before every message).
Current state · checked 2026-06-09
| Where | ~/Documents/midi-harness — separate repo, own .claude/, NOT this vault |
| What's built | cmd/listener + cmd/hook (Go, stdlib, ~0ms hook startup) + 46-word system prompt |
| Real use | ~10 practice sessions logged (2026-05-30 → 2026-06-01) in session-drain.md |
| Uncommitted drift | CLAUDE.md edited; future-ideas.md + session-drain.md untracked; repo has 1 commit |
| Parked on purpose | timing/rhythm capture (future-ideas.md) — harmony-read gets solid first |
Lineage
abandoned predecessor ·
~/Documents/midi-coach · github.com/jakesimonds/MCP-Dash-Demo
→
the 2026-05-29 strip-back → midi-harness
this node + the live repo
Both predecessor nodes archived 2026-06-09 (archive/midi-coach, archive/midi-harness-rebuild). This directory also still holds a dormant earlier experiment: a Rust synth + Python listener — see midi-harness.md.