Skip to content

live object model

nate
Aug 23, 20263 min read

live object model

the Live Object Model (LOM) is the python API a MIDI remote script (or Max for Live) gets to the running set: song, tracks, devices, parameters, clips, transport. it is complete enough to drive a mix, with a few hard edges.

every LOM call belongs on Live's main thread

the LOM is not thread-safe. a remote script that opens a socket and serves commands from another thread must marshal each command back with ControlSurface.schedule_message(delay_ticks, callback) and hand the result back through a queue. driving transport (start_playing, seeking) from a socket thread has crashed Live outright; reads from off-thread appear to work until they don't. one rule — everything through schedule_message — costs nothing and removes the whole class of bug.

a recurring main-thread job (metering, recording) is a callback that re-arms itself with schedule_message(1, self); one Live timer tick is roughly 100 ms.

a package reloads, its __init__ doesn't

Live imports the remote-script package once at launch and keeps it cached: toggling the control surface off and on does not re-import. importlib.reload on a submodule works fine. so keep __init__.py to the socket server and a reload command, put every handler in a sibling module, and iterate without restarting Live. a change to __init__.py itself still needs a restart.

property writes can read back stale

setting e.g. song.current_song_time and reading it in the same callback can return the old value; the next tick sees the new one. verify on the next command, not inline.

start_playing ignores a prior seek

start_playing() starts at the start marker regardless of where current_song_time was set. setting current_song_time while playing jumps playback. so "play from beat N" is: start, then seek, in one main-thread task. continue_playing() resumes from where playback last stopped, also ignoring the seek.

what is and isn't exposed

  • routing — tracks and sidechain-capable devices (Compressor, Gate, …) expose input_routing_type / input_routing_channel plus the available_* lists, each with a display_name. set the type first; the channel list depends on it.
  • meters — tracks have output_meter_left/right/level (0.0–1.0, peak, Live-smoothed, no documented dB mapping). the main track's input meter reads 0. devices expose no gain-reduction meter.
  • automation — a parameter reports automation_state (0 none, 1 playing, 2 overridden) but the arrangement lane's breakpoints are not readable; see als file format. Clip.automation_envelope / create_automation_envelope are session-clip only — on an arrangement clip Live answers Not a session clip. writing a lane is possible only by recording it, see automation via recording.
  • undo — song.begin_undo_step() / end_undo_step() group a scripted change into one undo; song.undo() reverts it.
  • dir() works on LOM objects: a generic "introspect this path" command is the fastest way to find out what a given Live version exposes before building a tool on an assumption.

sources

  • github.com/zzstoatzz/ableton-mcp — AbletonMCP_Remote_Script/, 2026-08-23 (crash, reload split, seek quirk, routing/meter/automation probes against Live 12.4.3)

Did you enjoy this article?

Recommend it — Standard Reader surfaces well-loved writing to more readers across the network.

Across the AtmosphereDiscussions