๐ŸŽน gridi

route based midi generation

readme

Route based midi generation.

quickstart

npm start
http://localhost:8080

Web MIDI needs a secure context, so use localhost or https. Any static server works; there is nothing to build.

npm test

Open 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

  1. Open a virtual MIDI port: IAC Driver on macOS, loopMIDI on Windows.
  2. Press Enable MIDI, pick slot A, choose that port.
  3. Set the DAW to receive on it, and to external sync if it should follow the tempo.
  4. Select the line feeding your Note nodes, set channels to Set here, pick the channels.

drive two instruments from one pulse

  1. Bind slot A and slot B to different devices.
  2. Select the line, Set here, pick output A and its channels.
  3. Switch the output selector to B and pick its channels.
  4. Give the B channels a transpose if the second instrument sits in another register.

send a filter sweep

  1. Place an LFO and a Param node, and patch LFO into Param.
  2. On the Param node, set Scope to CC and Controller to the number the instrument listens on โ€” 74 is filter cutoff by convention.
  3. Select the line between them, Set here, pick the output and channel.
  4. On the LFO, set Cycle to the sweep length and From/To to the range.
  5. Patch a Pulse into the LFO to restart the sweep in time.

follow another sequencer

  1. Choose the master's port under MIDI in.
  2. Press Sync. The tempo readout follows the master and the project BPM is left alone.
  3. Press play on the master.

play the patch from a keyboard

  1. Choose the keyboard's port under MIDI in.
  2. Place a MIDI In node and patch it into a Note node.
  3. Set Played note to Key to retune the patch from the pitch played, Transpose to shift it, Gate to ignore the pitch.
  4. 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.