"When you add an output, branch at the leaf — never fork the scheduler."
The Feature: Play a Real Synth, Live
The v1 engine can play the solo and chords out through Web MIDI — driving a real software/hardware instrument (a synth fed over the macOS IAC bus) so it sounds in time alongside the in-app audio. That's a satisfying feature on its own. But the interesting part is how it was added, because the wrong way is so tempting.
The Tempting Mistake: Fork the Scheduler
The naive way to add MIDI output is to give MIDI its own scheduler — its own loop, its own timing — running alongside the audio one. Now you have two schedulers, and the two outputs drift apart exactly like the independent voices in the last lesson. You'd have re-created the jitter problem one level up. The fix you just learned (one clock) would be quietly undone by forking the decider.
The Right Way: One Scheduler, Many Sinks
Instead, the single scheduler still decides when. Outputs differ only in how they emit: a Web Audio sink plays a sample, a Web MIDI sink sends note-on bytes. Adding MIDI added a sink, not a scheduler. This is the API-first / single-source idea from Track 4, now at the playback layer: one decider, many consumers, and you branch only at the leaf where the actual emission differs. The proof it works: the same scheduler that drives the in-app audio also drives a hardware synth, in time, with no second timing engine.