js2max
v1.1.2
Published
Compile JavaScript files into Max for Live (.amxd) devices
Maintainers
Readme
js2max
Write Max for Live devices in JavaScript. Compile to .amxd and drag into Ableton Live.
js2max is a Node.js compiler that takes a JavaScript file written for the Max 9 v8 engine and produces a ready-to-use .amxd device. No visual patching required.
Why?
The Max/MSP visual editor is great for experimentation but painful for serious development:
- No version control (binary/JSON diffs are unreadable)
- No code review, no linting, no refactoring tools
- AI coding assistants can't help you patch
- Copy-paste logic between devices is manual and error-prone
Max 9 introduced the v8 object — a modern V8 JavaScript engine with full ES6+ support and access to the Live Object Model. Your logic is pure JavaScript; you just need a minimal patch to wire it up.
js2max generates that patch for you.
Quick Start
npx js2max compile effect.js # → effect.amxd (drag into Ableton Live)
npx js2max compile effect.js -o effect.maxpat # → JSON for Max 9Installation
npm install -g js2maxOr use directly with npx:
npx js2max compile <input.js> [options]Usage
Write your JavaScript
// @device midi-effect
inlets = 2;
outlets = 1;
function msg_int(value) {
if (inlet === 0) {
// Control message
post("Received:", value, "\n");
return;
}
// MIDI byte — pass through doubled
outlet(0, value);
}
function bang() {
outlet(0, "hello", "world");
}Compile it
# MIDI effect (default) → .amxd
js2max compile effect.js
# Audio effect
js2max compile delay.js --type audio-effect
# Instrument
js2max compile synth.js --type instrument
# Output as .maxpat instead
js2max compile effect.js -o effect.maxpatCLI Options
| Option | Default | Description |
| ------------------------- | ------------------- | -------------------------------------------------------- |
| -o, --output <path> | <input>.amxd | Output file path (.amxd or .maxpat) |
| -t, --type <type> | midi-effect | Device type: midi-effect, audio-effect, instrument |
| --no-embed | (embeds by default) | Reference external .js file instead of embedding |
| -w, --device-width <px> | 400 | Max for Live device strip width |
Output Formats
.amxd(default) — Binary Max for Live device. Opens directly in Ableton Live (any version with Max for Live support)..maxpat— JSON Max patch. Opens in Max 9. Use-o file.maxpatto select this format.
The .amxd binary format was reverse-engineered from Ableton's own maxdevtools and validated against working devices. It uses a chunk-based container (ampf → meta → ptch with mx@c header and dlst directory listing).
Decorator Comments
Use comments at the top of your file to configure compilation:
// @device midi-effect — sets device type (overridden by --type flag)
// @inlet 0 "MIDI input" — help text for inlet 0
// @inlet 1 "Control" — help text for inlet 1
// @outlet 0 "Processed MIDI" — help text for outlet 0@ui — Device Strip UI Elements
Add interactive controls to your device's presentation view with @ui decorators. The compiler generates Max UI objects, wires them to your v8 inlets/outlets, and auto-layouts them in the device strip.
// @ui live.text "Fire" trigger inlet=0 — button (sends bang on click)
// @ui live.dial "Delay" inlet=1 min=0 max=1000 — rotary dial
// @ui live.slider "Volume" inlet=2 min=0 max=127 — vertical slider
// @ui live.toggle "Active" outlet=0 — LED toggle (driven by code)Format: // @ui <maxclass> "<label>" [trigger] inlet=N|outlet=N [min=X] [max=Y]
| Parameter | Description |
|-----------|-------------|
| inlet=N | Wires UI output to v8 inlet N (user controls the code) |
| outlet=N | Wires v8 outlet N to UI input (code controls the display) |
| trigger | For live.text — button mode (bang on click) instead of toggle |
| min/max | Value range for dials and sliders |
Example — MIDI echo with UI:
// @device midi-effect
// @ui live.dial "Delay" inlet=0 min=0 max=1000
// @ui live.dial "Feedback" inlet=2 min=0 max=100
// @ui live.toggle "Activity" outlet=1
inlets = 3;
outlets = 2;
function msg_int(value) {
if (inlet === 0) { delayTime = value; return; }
if (inlet === 2) { feedback = value / 100; return; }
outlet(0, value);
}Elements are arranged in a row below the title and wrap automatically when they exceed the device width.
How It Works
- Parses your
.jsfile to extractinlets,outlets, handler functions, and decorator comments - Selects a template based on device type (MIDI effect, audio effect, or instrument)
- Generates the patch JSON with the
v8object wired to the appropriate Max for Live infrastructure (midiin/midiout,plugin~/plugout~,live.thisdevice) - Embeds your JavaScript source directly in the patch (single-file distribution)
- Wraps in the
.amxdbinary container (chunk-based:ampf+meta+ptchwithmx@cheader anddlstdirectory)
Examples
See the examples/ directory:
- hello-world.js — Simplest possible device with a "Say Hello" button
- midi-echo.js — MIDI delay/echo with Delay/Feedback dials and Activity toggle
- clip-launcher.js — Live Object Model with Track/Slot dials and Fire/Stop buttons
Max 9 v8 API Quick Reference
The v8 object gives you access to these globals:
// Inlets and outlets (must be in global scope)
inlets = 2;
outlets = 3;
inlet; // read-only: which inlet triggered the current function
// Output
outlet(n, ...args); // send message out outlet n
outlet_array(n, arr); // send JS array (v8 only)
outlet_dictionary(n, dict); // send dictionary (v8 only)
// Message handlers (define as global functions)
function bang() {}
function msg_int(n) {}
function msg_float(f) {}
function list(...args) {}
function anything(msg, ...args) {} // catch-all
// Live Object Model
var api = new LiveAPI(callback, "live_set tracks 0");
api.get("name");
api.set("mute", 1);
api.call("fire");
// Utilities
post("debug message\n"); // print to Max consoleFull reference: Max JavaScript User Guide | v8 Reference | Live API
Limitations
- No audio DSP: JavaScript runs in Max's low-priority thread. Use
v8for MIDI processing, generative tools, control logic, and Live Object Model access — not for real-time audio processing.
Development
git clone https://github.com/ktamas77/js2max.git
cd js2max
npm install
npm run build
npm testCode Quality
Pre-commit hooks (via husky) automatically run on every commit:
- TypeScript typecheck (
tsc --noEmit) - Prettier formatting
- ESLint linting
npm run typecheck # type check
npm run lint # eslint
npm run format # prettier --writeChangelog
See CHANGELOG.md for release history.
License
MIT
