Ableton Live's MIDI Learn covers 95% of what most producers need. The remaining 5% is the interesting bit — per-stick curves, conditional routing, latched touchpad snapshots, scenes that fire from button combos. That is Max for Live's territory. This guide builds a real max for live gamepad input device, the kind that feels like it shipped with Live: native dials, mappable parameters, no spaghetti patch cables visible to the end user. You get a .amxd file at the end you can drop on any track.
- What you build: a custom
.amxdMIDI device that reads from Universal Controller MIDI and exposes mappable Live dials. - What it does: stick smoothing, trigger curves, touchpad XY, button-combo macros, rumble feedback.
- Requires: Ableton Live 12 Suite (or Standard + Max upgrade).
- Why bother: mappable native dials beat raw CC for usability tenfold.
Why a custom device beats raw MIDI Learn
Pure MIDI Learn in Live binds a single CC to a single parameter. Fine for "right stick X → cutoff". Useless the moment you want "right stick X is normally cutoff, but when L1 is held it becomes resonance". You either need a Rack with chain selectors (ugly), or you build a max for live gamepad device that does the conditional inside. The device approach also gives you saveable presets, named parameters in Live's automation lane, and clip envelopes. The official Cycling '74 Live overview is the right doc to keep open.
What you'll need
- Ableton Live 12 Suite — Suite includes Max for Live by default.
- Max 8.5+ (bundled with Suite — no separate install).
- Universal Controller MIDI v1.0+ — the bridge.
- DualSense, Xbox Series, or Switch Pro controller.
- About 30 minutes for a working v1.
Step-by-step build
1. Start with a Max MIDI Effect
In Live, drag Max MIDI Effect from the Max for Live browser onto any track. Click the small Edit icon. Max opens with an empty patch and a couple of placeholder live.dials. Delete the placeholders — we want a clean canvas.
2. The input chain
Drop a midiin object. Set its inlet to Universal Controller MIDI Bridge from the dropdown. Wire it into a midiparse. The midiparse object splits incoming MIDI into typed outlets — notes go out the first outlet, polyphonic aftertouch the second, control changes the third, program changes the fourth, channel pressure the fifth, pitchbend the sixth.
// Conceptual patch wiring
[midiin "Universal Controller MIDI Bridge"]
|
[midiparse]
| | |
notes CC pitchbend
|
[unpack 0 0] // splits CC into [number, value]
| |
[route 20 21 22 23 2 11 16 17] // pluck out our gamepad CCs
| | | | | | | |
stk-Lx Ly Rx Ry L2 R2 TpX TpY 3. Smoothing each axis
Drop a slide object on each stick outlet. The slide object is Max's exponential smoother — slide 5 5 gives gentle rounding both directions. For a one-euro behaviour, build a small subpatch that adapts the smoothing based on velocity, but slide alone is fine for v1.
4. Trigger curves with expr
Triggers come in on CC 2 and CC 11. After the route, send the value through:
// In the patch:
[scale 0 127 0. 1.] // 7-bit CC to float
|
[expr pow($f1, 2.)] // squared curve
|
[scale 0. 1. 0. 1.] // stays a float for the dial
|
[live.dial] The squared curve gives a 1.8-ish feel that suits most modulation rides. For different curves swap the exponent — 2.5 for dramatic ducks, 0.7 for inverse expression.
5. Live.dial — the mappable bit
This is the secret sauce. Drop a live.dial for each parameter you want to expose. Right-click each → Inspector → set Parameter Visibility to Stored and the Long Name to something readable (Stick L X, Trigger R, Touch X). Live picks up these dials and shows them in MIDI Map mode, parameter automation, and clip envelopes. To users it feels like a native device.
6. Button combo macros
The bridge sends face buttons as note-on / note-off messages on Notes 60–63. Inside the patch, drop a notein, gate the outlet through a change, and use if objects to detect combos. Example: "if L1 held while right stick moves, swap the right stick CC routing from cutoff to resonance":
// Pseudo-patch for L1+stick conditional routing
[notein] → [stripnote] → [sel 66] // L1 = Note 66
|
[toggle l1_held]
// In the CC chain:
[ctlin 22 1] // right stick X
|
[gate 2 1] // gate based on l1_held: 0 = cutoff, 1 = reso
↑
[l1_held]
|
[+ 1] // toggle becomes 1 or 2
| |
[cutoff] [resonance]
[live.dial][live.dial] 7. Save as .amxd
File → Save in Max. Name it Gamepad Input.amxd. Live now treats it as a regular device — it shows up in the User Library and you can save it as a default. Drop it on the master track and every project loads with gamepad input pre-wired.
Exposed parameters at a glance
| Dial name | Source CC | Range | Typical map target |
|---|---|---|---|
| Stick L X | CC 20 | -1 to 1 (bipolar) | Pan, filter LFO offset |
| Stick L Y | CC 21 | -1 to 1 | Macro 1 |
| Stick R X | CC 22 | -1 to 1 | Filter cutoff |
| Stick R Y | CC 23 | -1 to 1 | Resonance / macro 2 |
| Trigger L | CC 2 | 0 to 1 (curved) | Reverb send |
| Trigger R | CC 11 | 0 to 1 (curved) | Wavetable position |
| Touch X | CC 16 | 0 to 1 | XY pad X |
| Touch Y | CC 17 | 0 to 1 | XY pad Y |
| L1 Modifier | Note 66 | 0 or 1 (held) | Conditional routing flag |
Live API for the polish
Max for Live ships with a Live API that lets your device introspect the song. A live.thisdevice + live.path pair can read the current scene, the selected track's name, even the BPM. Use it to make the gamepad context-aware: "right stick controls the selected device's first macro, always". This is a one-liner with the live.observer object:
[live.path live_set view selected_track view selected_device]
|
[live.object id]
|
[live.observer parameter1 value] // pipe value into the dial Now the gamepad always follows your selection. No remapping per session, no template swapping.
Haptic feedback on the master bus
Pop an envelope follower on the master output. Pipe its level into a live.dial named Master RMS. From that dial, route to a midiout aimed back at the bridge's feedback port, sending CC 100 for rumble. Result: controller pulses with the kick drum. The mix feels alive. Full detail in our haptic feedback piece.
Gotchas worth knowing
- Max 8.5 vs Max 9. Live 12 ships with Max 8.5. The Live API has stayed stable for years but the patch above assumes 8.5+ object versions.
- Live.dial values are floats. If you connect a 0–127 integer CC directly to a dial, you get terrible step-quantised UI feedback. Scale to
0. 1.first. - Don't forget Mappable on every dial. Without it, Live's MIDI Map mode won't see the parameter and your device is useless.
- Save as a Live Pack if you want to share it. The
.amxdfile plus dependencies bundles into a single.alp.
Ableton's flexibility is famous; Max for Live is what makes it ridiculous. Pair it with a gamepad bridge and you have a controller that adapts to the project — not the other way around.