Field manual & progression chart — Go 1.26
Twenty-two learning points, three tiers, and a MIDI/OSC router for your own rig at the end. Nodes unlock when their prerequisites clear, and a node only clears on evidence — a command that runs, a test that passes, a race the detector cannot find.
The 2–3 day project · nodes 16–22
A MIDI/OSC router you can put in a live set. It replaces pendulum/sender/midi-to-osc.mjs and then goes past it: every controller at once, several destinations at once, a musical clock, a routing table you can rewrite from your phone mid-set, and a panic button that actually stops the sound. You already know this signal flow, so all the difficulty lands on Go rather than on the problem.
// router.go type Event struct { Addr string // "/midi/cc/74" Args []float32 From string // "nord", "osc:9000", "clock" } type Router struct { mu sync.RWMutex routes []Route dests map[string]chan Event } func (r *Router) Route(e Event)Illustrative — you write the bodies
GOOS=linux GOARCH=amd64 go build produces a static Linux ELF from your Mac, verified. The MIDI half is not — gomidi's rtmidi driver is a C++ library through cgo, and that same command fails with build constraints exclude all Go files. It compiles and runs natively here in a few seconds against Xcode's clang, and IAC Driver Bus 1 is already enabled, so Ableton needs no setup at all. If you ever want a headless OSC-only node on the performance network, put the MIDI driver behind a build tag. That split is the honest shape of the tradeoff, not a workaround.