readme
Route based midi generation.
quickstart
npm starthttp://localhost:8080
Web MIDI needs a secure context, so use localhost or https. Any static server
works; there is nothing to build.
npm testOpen http://localhost:8080/tests/audio-check.html for the synth checks, which
need a browser.
nodes
| Node | In | Out | Does |
|---|---|---|---|
| Pulse | โ | many | Emits on a beat at project BPM. Owns the feel and the starting key. |
| MIDI In | โ | many | A note played on an attached keyboard sends a pulse from here. |
| Split | 1 | many | Sends one pulse down every outgoing line at once. |
| Logic | many | many | Fires when incoming pulses line up inside a window. |
| Chance | 1 | many | Lets a pulse through a set percentage of the time. |
| Router | 1 | many | Sends each pulse down a single outgoing line. |
| Note | 1 | many | Fires a scale degree as MIDI on every channel the line carries. |
| Voice | 1 | many | Two oscillators, a resonant filter and an ADSR, in the browser. |
| LFO | 1 | many | Sends a moving value down its lines many times a beat. |
| Param | 1 | many | Changes a value instead of firing a note, then passes the pulse on. |
| Key | 1 | many | Rewrites scale and key for everything downstream. |
Pulse
| Control | Range |
|---|---|
| Active | on / off |
| Division | 1/1, 1/2, 1/4, 1/8, 1/16, 1/32, 1/2T, 1/4T, 1/8T, 1/16T, 1/4., 1/8. |
| Swing | 0โ60%, delays every second step |
| poly steps / in the time of | 1โ16 each, steps against divisions |
| Humanize | 0โ40 ms |
| Euclid | hits, steps, rotate |
| Key from | project / this node |
| Base ch | 1โ16 |
| Velocity | 1โ127 |
MIDI In
| Control | Range |
|---|---|
| Listen on | 0โ16, zero is omni |
| Played note | Key, Transpose, Gate |
| Centre | 0โ127, the note that means no transposition |
| Use velocity | on / off |
| Base ch | 1โ16 |
Split, Logic, Chance, Router
| Node | Control | Range |
|---|---|---|
| Split | Stagger | 0โ0.5 beat between branches |
| Logic | Mode | AND, OR, N, XOR |
| Logic | Needs | 1โ8, in N mode |
| Logic | Window | 1โ120 ms |
| Chance | Pass | 0โ100% |
| Chance | Mode | Free, Drift |
| Router | Mode | Cycle, Ping, Rand, No rpt |
Note
| Control | Range |
|---|---|
| Degree | โ21 to 22 |
| Octave | โ1 to 8 |
| Overflow | Extend, Fold, Clamp |
| Velocity | 0โ127, zero follows the line |
| Length | 0.02โ4 beats |
| Ratchet | 1โ8 |
| Send MIDI | on / off |
| Audible | on / off, the built-in blip |
Voice
| Control | Range |
|---|---|
| Degree, Octave, Overflow | as Note |
| Wave A / B | sine, triangle, square, sawtooth |
| Octave A / B | โ3 to 3 |
| Semitones A / B | โ12 to 12 |
| Detune A / B | โ50 to 50 cents |
| Level A / B | 0โ1 |
| Cutoff | 80โ12000 Hz |
| Reso | 0.1โ20 |
| Env depth | 0โ4 octaves |
| Attack | 0.001โ2 s |
| Decay | 0.005โ2 s |
| Sustain | 0โ1 |
| Release | 0.005โ3 s |
| Gate | 0.02โ4 beats |
| Level | 0โ1 |
| Voices | 1โ32, oldest stolen past the limit |
LFO
| Control | Range |
|---|---|
| Shape | Sine, Triangle, Ramp, Saw, Square, Random, Drift |
| Cycle | 1/16, 1/8, 1/4, 1/2, 1 bar, 2 bars, 4 bars, 8 bars |
| Phase | 0โ100% of a turn |
| Depth | 0โ100% |
| From / To | 0โ127 each |
| Steps | 1โ48 values per beat |
| Reset on pulse | on / off |
| Base ch | 1โ16 |
An LFO is patched into a Param node, which sends the value. Note, Voice, Logic, Chance and Router ignore waves; Split, Key and Param act on them.
Param
| Control | Range |
|---|---|
| Scope | Node, Signal, CC |
| Target / Param | in Node scope |
| Param | Velocity, Transpose, Degree shift, in Signal scope |
| Controller | 0โ127, in CC scope |
| Mode | Seq, Rand, Walk, Add |
| Values | space separated, in Seq mode |
| Amount | in Add and Walk modes |
| Min / Max | outside Seq mode |
Key
| Control | Range |
|---|---|
| Mode | Set, Cycle, Rand |
| Key / Scale | in Set mode |
| Changes | root:scale pairs, e.g. 0:minPent 5:major |
| Transpose | โ24 to 24 semitones |
| Latch | writes the project key |
lines
A line carries state, not just a connection.
| Property | Behaviour |
|---|---|
| Channels | A set, each with its own output, transpose and velocity. One pulse fans out across all of them. |
| Channel mode | Inherit passes on what arrived; Set here overrides from this line down. |
| Scale and key | Same two modes. Set here retunes everything the line feeds. |
| Delay | 0โ4 beats. The only thing that shifts timing; grid distance never does. |
| Mute | Stops passing pulses without unpatching. |
Scales: Major, Nat Minor, Harm Minor, Mel Minor, Dorian, Phrygian, Lydian, Mixolydian, Locrian, Maj Pent, Min Pent, Blues, Whole Tone, Chromatic.
Degree overflow: Extend carries past the end of the scale into the next
octave, Fold wraps inside one octave, Clamp stops at the top.
MIDI
| Control | Where | Does |
|---|---|---|
| Enable MIDI | header | Requests access. Needs a user gesture and a secure context. |
| A B C D | header | Picks which output slot the device list and Clk apply to |
| Device list | header | Binds a device to that slot |
| Clk AโD | header | Whether that output receives clock |
| Clock | header | Whether clock is sent at all |
| Panic | header | All notes off on every output |
| MIDI in device | header | The port clock and played notes arrive on |
| Sync | header | Follow the incoming clock instead of the project tempo |
Outputs are named slots. A patch stores the slot letter; each machine binds its own devices, remembered between sessions.
Sent: note on, note off, control change, clock at 24 PPQN, start, stop, continue, song position.
Received: clock, start, stop, continue, song position, note on.
On a single channel, note length is capped by the retrigger rate โ two instances of one pitch on one channel cannot overlap. Use different pitches or channels.
controls
| Action | Control |
|---|---|
| Play / stop | Space, or Play |
| Place a node | Click a type in the left rail, then click the grid. Shift keeps placing. |
| Patch two nodes | Drag from a node's right edge onto another node |
| Select | Click a node or a line |
| Move a node | Drag it |
| Pan | Drag empty grid, or alt-drag |
| Zoom | Wheel |
| Fit to patch | F |
| Delete selection | Del or Backspace |
| Duplicate node | D |
| Mute selected line | M, or double-click the line |
| Undo / redo | โZ / โงโZ |
| Save patch | โS, or Save |
| Cancel | Escape |
New, Demo, Save, Open and the theme toggle are in the left rail. Patches autosave to local storage.
limits
Past these, Gridi drops what it cannot carry and says so in a banner.
| Limit | Ceiling |
|---|---|
| Scheduler events | 2000 per tick, 8000 per second sustained |
| MIDI messages | 2000 per second, per device |
| Sounding voices | 64 across the patch |
| Patch size | 400 nodes, 800 lines |
Repeated overloads stop the transport. Note-offs and clock are never dropped.
task recipes
drive a DAW
- Open a virtual MIDI port: IAC Driver on macOS, loopMIDI on Windows.
- Press Enable MIDI, pick slot A, choose that port.
- Set the DAW to receive on it, and to external sync if it should follow the tempo.
- Select the line feeding your Note nodes, set channels to Set here, pick the channels.
drive two instruments from one pulse
- Bind slot A and slot B to different devices.
- Select the line, Set here, pick output A and its channels.
- Switch the output selector to B and pick its channels.
- Give the B channels a transpose if the second instrument sits in another register.
send a filter sweep
- Place an LFO and a Param node, and patch LFO into Param.
- On the Param node, set Scope to CC and Controller to the number the instrument listens on โ 74 is filter cutoff by convention.
- Select the line between them, Set here, pick the output and channel.
- On the LFO, set Cycle to the sweep length and From/To to the range.
- Patch a Pulse into the LFO to restart the sweep in time.
follow another sequencer
- Choose the master's port under MIDI in.
- Press Sync. The tempo readout follows the master and the project BPM is left alone.
- Press play on the master.
play the patch from a keyboard
- Choose the keyboard's port under MIDI in.
- Place a MIDI In node and patch it into a Note node.
- Set Played note to Key to retune the patch from the pitch played, Transpose to shift it, Gate to ignore the pitch.
- Start the transport; played notes need it running.
tech
Vanilla ES modules. No build, no dependencies. Web Audio for the built-in voices, Web MIDI for output and input.
npm test runs 220 tests under node --test. engine, model, music,
rhythm, voice, sync, lfo, limits and geometry have no DOM, audio or
MIDI dependencies and are tested directly; the engine runs against a fake clock
and stub outputs. The synth is checked in a browser at
tests/audio-check.html.
Web MIDI: Chrome, Edge, Opera, Firefox 108+. Not Safari and not any iOS browser, which all run WebKit โ those get the built-in voices only.