Summary
Let Modular Riffs listen to a MIDI controller, not just send to one. Today js/midi.js is output-only (notes + clock + transport). Open the MIDI inputs so a hardware keyboard or control surface can drive the jam hands-free: select the live chord, nudge key/scale, run the transport, and ride the macro knobs (Evolve / Energy / per-part Activity) — all while your hands stay on the gear.
Why it fits
Modular Riffs is meant to be performed, not just configured. When you're patching cables with both hands, reaching for the mouse to reroll a section or lean on the energy is the thing that breaks flow. A controller mapping closes that gap and turns the app into a playable front-end for the rig it's already clocking.
Proposed scope
Input plumbing (js/midi.js)
requestMIDIAccess already exposes access.inputs; add listInputs(), an input picker, and onmidimessage handling alongside the existing output code.
- Parse Note On/Off, CC, and (optionally) transport bytes. Ignore the app's own clock if it's echoed back.
Dispatch (js/app.js)
- Route incoming events into the existing state + performable edit queue, so chord/section changes land on the next bar boundary (the quantize hook already lives in
js/scheduler.js) rather than glitching mid-bar.
- Targets, in priority order:
- Chord degree / quality — keys or pads pick the current chord (I–VII + 7th/sus/6 toggles); punches in over the arrangement at the next boundary. (Foundation for a future live chord-pad UI.)
- Transport — Play / Pause / Stop, plus Reroll-section / next-section as note or CC triggers.
- Key & scale — step the root around the circle of fifths; cycle the scale.
- Macros — map CCs to Evolve, Energy, Swing, and each part's Activity / mute / gate.
MIDI-learn + a documented default map
- A small "MIDI Learn" affordance: click a control, wiggle a knob, it binds. Persist bindings in
state (already plain JSON, so it rides presets/export).
- Ship a documented default CC map in the README (a compact table) so a generic controller works out of the box.
Where
js/midi.js (input handling), js/app.js (dispatch + learn UI + persistence), README (CC map table).
Done when
Open questions
- Default chord layout: white-keys-as-degrees, or a dedicated pad bank?
- Should transport-in also imply clock-in (slaving), or keep that a separate effort?
Summary
Let Modular Riffs listen to a MIDI controller, not just send to one. Today
js/midi.jsis output-only (notes + clock + transport). Open the MIDI inputs so a hardware keyboard or control surface can drive the jam hands-free: select the live chord, nudge key/scale, run the transport, and ride the macro knobs (Evolve / Energy / per-part Activity) — all while your hands stay on the gear.Why it fits
Modular Riffs is meant to be performed, not just configured. When you're patching cables with both hands, reaching for the mouse to reroll a section or lean on the energy is the thing that breaks flow. A controller mapping closes that gap and turns the app into a playable front-end for the rig it's already clocking.
Proposed scope
Input plumbing (
js/midi.js)requestMIDIAccessalready exposesaccess.inputs; addlistInputs(), an input picker, andonmidimessagehandling alongside the existing output code.Dispatch (
js/app.js)js/scheduler.js) rather than glitching mid-bar.MIDI-learn + a documented default map
state(already plain JSON, so it rides presets/export).Where
js/midi.js(input handling),js/app.js(dispatch + learn UI + persistence), README (CC map table).Done when
Open questions