原始内容
name: ableton-extensions description: Scaffold, write, build, and package Ableton Live extensions with the Ableton Extensions SDK (@ableton-extensions/sdk, TypeScript). Use when creating or editing an Ableton Live extension — adding context-menu actions, modal/progress dialogs, or code that manipulates tracks, clips, MIDI notes, devices, tempo, audio rendering, or selections in a Live Set.
Ableton Extensions SDK
Build tools that run inside Ableton Live. An extension is a Node/TypeScript module bundled to a single CommonJS file that Live's Extension Host loads. It registers commands and wires them to context-menu actions, then reads and mutates the Live Set through a typed object model (tracks, clips, notes, devices, tempo, scenes).
Extensions are an offline editing / automation layer. They are not real-time processors and not devices in the signal chain: they cannot process the live audio or MIDI stream. The SDK has no note-on/off, MIDI-input, or stream callbacks (the only on* callbacks are dialog/registration lifecycle internals) — an extension only acts when the user triggers a command, then mutates the data model. For real-time MIDI/audio transformation (e.g. "play one key, hear a chord"), the right tool is a MIDI/audio effect device in the track chain: Live's built-in MIDI effects (the Chord device does exactly one-note→chord — set Shift voices to e.g. +4, +7) or a Max for Live device — not an Extension. Extensions complement those by transforming clips/notes at edit time, batch-editing the Set, and adding context-menu tools, dialogs, and rendering.
Local SDK location
The SDK distribution lives wherever you unpacked it — referred to below as <sdk-dir>. It contains:
docs/— pre-built HTML documentation (getting-started, essentials, development, design)api/— TypeDoc HTML API referenceexamples/— 7 runnable examples (read these for canonical patterns)ableton-create-extension-<version>.tgz— the scaffolding toolableton-extensions-cli-<version>.tgz— the build/run/package CLIableton-extensions-sdk-<version>.tgz— the SDK library
This skill was written against SDK 1.0.0-beta.0 (API version "1.0.0"); newer builds may ship a different <version> — check the actual .tgz filenames in <sdk-dir> and substitute it wherever it appears below. Requires Node.js >= 24.14.1.
Reference files (load as needed)
- reference/scaffolding.md — create a project, the generated file layout,
manifest.json/package.json/build.ts/tsconfig.json, theextensions-cli run|packagecommands, the.env/EXTENSION_HOST_PATH, debugging, log file locations, packaging to.ablx. - reference/api.md — the full object model:
ExtensionContext, handles, transactions,Song/Track/Clip/MidiClip/AudioClip/Device/Scene, MIDINoteDescription, enums (WarpMode,GridQuantization), context-menu scopes, selection types. - reference/recipes.md — copy-paste code for every common task: context menus, modal dialogs (with the WebView bridge + Ableton-themed CSS), progress dialogs with cancellation, creating/editing clips and notes, audio import/render, transactions.
The examples/ folder is the source of truth for idiomatic code — prefer reading the closest example to the task over guessing.
The entry point — always this shape
src/extension.ts exports activate. Call initialize once, then register commands and menu actions synchronously:
import { initialize, type ActivationContext } from "@ableton-extensions/sdk";
export function activate(activation: ActivationContext) {
const context = initialize(activation, "1.0.0"); // ExtensionContext
// 1. Register a command (the logic)
context.commands.registerCommand("myext.doThing", (arg: unknown) => {
// arg is a Handle, ArrangementSelection, or ClipSlotSelection
// depending on the scope the menu action was registered for
});
// 2. Surface it as a right-click menu item (the trigger)
context.ui.registerContextMenuAction("AudioClip", "Do Thing", "myext.doThing");
}
context (the ExtensionContext) is the root of everything:
context.application.song— the Live Set (tempo,tracks,scenes, …)context.commands—registerCommand(id, cb)/executeCommand(id, …)context.ui—registerContextMenuAction,showModalDialog,withinProgressDialogcontext.resources—importIntoProject(path), render audio from a trackcontext.environment—storageDirectory,tempDirectory,languagecontext.getObjectFromHandle(handle, Class)— turn aHandleinto a typed objectcontext.withinTransaction(() => …)— group mutations into one undo step
Two concepts that trip people up
Handles are not objects. Live passes commands lightweight Handles ({ id: bigint }). Resolve to a typed object with context.getObjectFromHandle(handle, Track). Handles go invalid when the object is deleted, moved in the arrangement, or the Set changes — never cache objects or handles across operations.
Commands receive scope-dependent args. Object scopes ("AudioClip", "MidiTrack", "ClipSlot", "Scene", …) pass a single Handle. Selection scopes pass a structured object: "AudioTrack.ArrangementSelection" / "MidiTrack.ArrangementSelection" pass an ArrangementSelection (time_selection_start, time_selection_end, selected_lanes: Handle[]); "ClipSlotSelection" passes { selected_clip_slots }. Match the scope you register to the arg type you cast.
Standard workflow
- Scaffold (new project):
npx file:<sdk-dir>/ableton-create-extension-<version>.tgz <dir>— interactive prompts for name, author, Live path, UI support. See reference/scaffolding.md. - Write
src/extension.ts— register commands + menu actions; see reference/recipes.md. - Run in Live:
npm start(builds with esbuild, thenextensions-cli runloads it into the Extension Host). Enable Developer Mode first in Live's Preferences → Extensions. - Debug:
console.log/warn/error→ExtensionHost.txt(paths in reference/scaffolding.md). In VS Code, F5 / "Debug Extension". - Package:
npm run package→ a distributable.ablxarchive users drop into Live.
Conventions that matter
- TypeScript, ESM project, CJS bundle.
package.jsonis"type": "module";build.tsbundles toformat: "cjs",platform: "node". Keep it that way — the Extension Host loads CommonJS. - Namespace command IDs (
"myext.action") to avoid collisions with other extensions. - Most mutations are async (
Promise). Batch related edits incontext.withinTransaction(() => promises)thenawait Promise.all(...)so the user gets one undo step and Live updates once. - Narrow handle types defensively with
instanceof(e.g. resolve toClip, then checkclip instanceof AudioClip) — a menu scope can match objects the code must reject. - HTML dialogs are inlined by esbuild (
loader: { ".html": "text" }) and passed as adata:text/html,URL — there's no web server.