One tiny Max for Live MIDI effect per instrument: it plays only the keys you give it, then transposes the result. Two things make it worth it:
- Transpose after the split β the split point never moves with it. The zone is filtered on the notes you actually play; the octave/semitone shift hits only the survivors, downstream. So you can transpose the sound as much as you like and the split boundary stays exactly where you set it β it never transposes itself. Define the split once, shift the sound freely on top.
- Mappable min/max bounds β which Ableton can't do natively.
LowandHighare real Live parameters, so the split point is MIDI-mappable and automatable (Ableton's built-in key zones are not). Map several Zones' bounds to one CC and move a split across instruments, live.
Stack instances to build splits and layers.
Bonus β light your splits on a WLED strip. Turn on Lights and each Zone paints its keyboard range as a coloured band on an LED strip, live, via the open wled-midi convention β see below.
- Download
zone.amxd(also listed on maxforlive.com). - Drop it on a MIDI track, before the instrument (or into a rack chain).
- Requires Live with Max for Live (Suite, or Standard + M4L). Built on Live 12 / Max 8.6.
The device is a few rows of controls (no on-screen keyboard). Every control is a Live
parameter, so it's automatable and MIDI-mappable β including the Low/High split bounds,
which Ableton's native key zones don't let you map.
| Field | Default | Impact |
|---|---|---|
| Bypass | off | On = hard bypass: every note passes through raw β no zone filter, no transpose. Off = normal. |
| Mute | off | On = blocks all note output (empty zone, nothing plays). Non-note MIDI still passes; held notes get their note-off so nothing hangs. |
A note plays only if it falls inside the zone. Each side has an on/off toggle, a value, and a Learn button.
| Field | Default | Impact |
|---|---|---|
| Low | 0 | The low bound. With Lo on, only notes β₯ Low pass. Its note name is shown beside it (C-2 at 0). |
| High | 127 | The high bound. With Hi on, only notes < High pass β the High note itself belongs to the upper zone, so adjacent split zones meet with no overlap. Note name shown (G8 at 127). |
| Lo on / Hi on | off | Enable each bound. A side that's off is open (no limit). Both off = full range, everything passes. |
| Learn Lo / Learn Hi | β | Click to arm, then play a note: that note becomes the bound. Editing is the learn. |
Defaults 0 / 127 with limits off = a fully-open zone (everything through); the 0/127 values are just neutral starting points for when you switch a limit on.
| Field | Default | Impact |
|---|---|---|
| Oct (Octave) | 0 | Coarse shift, Β±4 octaves. |
| Tone | 0 | Fine shift, β6β¦+5 semitones. Tiles exactly with Octave (no gap, no duplicate) β the Roland/Korg half-octave convention. Final pitch = note + OctΓ12 + Tone, clamped 0β127. |
Lets external CCs (a fader, a clip envelope, a script) move the Tone and/or Octave value. Handy when you author the CC by value (e.g. a clip sending CC to this track). Each has its own CC number and enable; the mapping mode is shared.
| Field | Default | Impact |
|---|---|---|
| Tone on / Oct on | on | Per-target master enable. On = the watched CC drives Tone / Octave and is consumed (never reaches the instrument). Off = that CC passes through untouched. |
| Tone CC# | 102 | Which CC number drives Tone, on any channel that reaches the track. 102β119 is the MIDI "Undefined" range β no collision with mod-wheel (1), expression (11), sustain (64), cutoff (74)β¦ (Channel is Ableton's track-input job β no channel field here.) |
| Oct CC# | 103 | Which CC number drives Octave (same rules). |
| Center | 64 | (shared) Where 0 sits in the CC value. 64 = value 64 β 0 (a window around 64). 0 = value 0 β 0 (wraps, so you reach the negatives even though a CC value never goes below 0). |
| Range | Step | (shared) Step = 1 CC value = 1 step (a window; the rest saturates or wraps). All = the whole 0β127 sweep is interpolated across the steps. |
The four modes (Center Γ Range) β shown for Tone (folded to β6β¦+5); Octave works identically with its own Β±4 range (9 steps, value 64 + n):
| Step (1 value = 1 semitone) | All (interpolate 0β127) | |
|---|---|---|
| Center 64 | window 58β69 β β6β¦+5, saturates outside β value 64 + n, no math |
ramp: value 0ββ6, 64β0, 127β+5 |
| Center 0 | wrap /12: 0β0, 1β+1, β¦, 5β+5, 6ββ6, β¦ | value 0β0 β¦ 127ββ1 (12 steps, no return to 0) |
Default 64 + Step = the window: value 68 β +4, 64 β 0, 58 β β6, 69 β +5, outside saturates.
Why a CC per target, with modes? Octave (9 values) and Tone (12 values) are far coarser than a
CC's 127 steps β a straight linear sweep wastes most of the range and lands between values. So each
shift gets its own CC, plus a shared choice of how the value maps onto its few slots β in
particular the window around 64, where value 64 = 0 and every step is one increment
(64 + n = n): trivial to author by hand and precise, instead of hunting for the right point on a
127-step fader.
Drive it from a Stream Deck. The Trevliga Spel Stream Deck MIDI plugin is an absolute gem β it fires MIDI (CC, Program Change, Noteβ¦) straight from physical Stream Deck keys. Assign a key to send
CC 102at a fixed value and you get one-press Tone recalls, or drive any of Zone's parameters hands-on. Honestly a killer companion for this device.
Everything that isn't a note passes through untouched β sustain, expression, pitch bend, aftertouch, program change, and any CC except the ones assigned to Tone / Octave (each consumed while its enable is on). Held notes always get their note-off, even if you move a bound, transpose, mute or bypass while they ring: no stuck notes.
Put one Zone at the head of each instrument and MIDI-map the bounds directly (Ableton's MIDI
map, Cmd-M) β no rack, no internal linking. Map several Zones' Low/High to the same CC and
one control moves the split across all of them at once:
| Stage-keyboard mode | With Zones |
|---|---|
| Solo (one sound everywhere) | limits off |
| Split (bass left / keys right) | Zone A High + Zone B Low on one CC = movable split point |
| Layer (two sounds together) | both full range (or both bypassed) |
| Split + layer | any mix β stack as many Zones as you like |
Zone can show each split on an LED strip β every instrument's keyboard range lit as a coloured
band, moving live as you move the split. It drives wled-midi,
the open MIDIβWLED convention, in its zone position mode.
How it works. Turn Lights on and Zone holds two notes β the zone's low and high
boundary β on the Lights Ch channel, sent to a MIDI port named OpenLamp. A wled-midi
implementation (the engine, the
browser tool, the Bome packβ¦)
listening on that port lights the LED band between the lowest and highest held note, in the channel's
colour. Move a bound β the band moves. Stack Zones on different channels β each instrument's range shows
in its own colour, a live map of your split.
| Control | Default | Impact |
|---|---|---|
| Lights | off | On = emit this zone's boundaries to the OpenLamp port (the WLED band). Off = nothing sent. Muted/bypassed zones clear their band. |
| Lights Ch | 1 | The MIDI channel the band is sent on = which colour in wled-midi's hand/zone map (ch 1 = hand 1, ch 2 = hand 2β¦). Give each Zone its own channel. |
A side with its limit off is open, so the band runs to the strip end (note 0 / 127) on that side β
exactly the range that actually plays. High is exclusive (the filter passes note < High), so the band
tops out at High β 1, flush with the last sounding key.
Setup (once). Create a virtual MIDI port named OpenLamp (macOS: Audio MIDI Setup β IAC Driver;
Windows: loopMIDI) β the same port the wled-midi
implementations listen on. Zone's internal [midiout OpenLamp] sends straight to it, independent of
Ableton's track routing (your instrument still gets the normal filtered notes). Point your wled-midi
implementation at your WLED device, set it to strip / zone mode, and turn Lights on.
The Lights output is additive and non-intrusive β it never touches the note stream going to your instrument. If the
OpenLampport doesn't exist, themidioutis simply silent; nothing else changes.
Interactive demo
(or open zone-demo.html locally) β move a macro, watch one split
point drive several zones.
| File | Purpose |
|---|---|
zone.amxd |
the device, ready to use β source of truth (hand-arranged layout + logic) |
zone.js |
the brain β zone filter, octave/tone transpose, Tone-via-CC, note tracking |
zone.maxpat |
the patcher inside the device (UI + wiring), kept in sync with zone.amxd |
gen_zone_maxpat.py |
the original scaffold that generated the first patch (reference only β see hacking notes) |
rack/ |
Zone Keyboard rack preset + demo set + docs |
midiin β midiparse β¬ notes βββββββββββββββ [js zone.js] β¬ outlet 0 β midiout (filtered + shifted notes β instrument)
β control-change βββββββ [js zone.js] β outlet 9 β midiout OpenLamp (zone boundaries β wled-midi `zone`)
β (Tone/Octave-via-CC: consumed, or re-emitted untouched)
β everything else β midiformat β midiout (untouched passthrough)
zone.js has 10 outlets: 0 = MIDI out (to the instrument), 1β2 = Low/High value feedback, 3β4 = note-name
labels, 5β6 = learn-state labels, 7β8 = Tone/Octave value (CC-driven), 9 = WLED lights (the zone's two
boundary note-ons β midiout OpenLamp). The lights path is independent of outlet 0, so it never disturbs
the notes reaching your instrument.
Hacking notes:
zone.jsis referenced, not frozen in the device, and runs withautowatch = 1: edit the file next to the device and Max hot-reloads it. (Adding/removing ajsoutlet needs a full device reload, not a hot-reload.)- The presentation layout is hand-arranged in Max and lives in
zone.amxdβ it is the source of truth.zone.maxpatis that device's patcher JSON, kept in sync. The.amxdis a 32-byteampfheader + the patcher JSON, so structural changes can be applied to it directly. gen_zone_maxpat.pyscaffolded the original structure. It does not reproduce the hand-tuned layout or the later additions β re-running it would reset the presentation. Keep it as a reference for how the wiring was first built; don't overwrite the shipped files with it.
Latest version, changelog and issues live here on GitHub β the maxforlive page is a pointer to this repo.
MIT β see LICENSE.
Splits for Live β and an optional bridge to light. Zone is a standalone Max for Live device; its optional WLED output speaks the open wled-midi convention β the agreed dictionary between MIDI (the MIDI Association) and WLED. Part of the OpenLamp ecosystem; free for anyone to build on.
