{"id":14007092,"url":"https://github.com/leolabs/ableton-js","last_synced_at":"2025-05-15T05:05:03.627Z","repository":{"id":38008436,"uuid":"187489144","full_name":"leolabs/ableton-js","owner":"leolabs","description":"Control Ableton Live with Node.js","archived":false,"fork":false,"pushed_at":"2025-03-12T19:04:31.000Z","size":779,"stargazers_count":424,"open_issues_count":6,"forks_count":51,"subscribers_count":17,"default_branch":"master","last_synced_at":"2025-04-07T01:05:42.539Z","etag":null,"topics":["ableton","api","javascript","js","live","ts","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/leolabs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["leolabs"],"ko_fi":"leolabs","custom":"https://paypal.me/leolabs"}},"created_at":"2019-05-19T14:44:42.000Z","updated_at":"2025-04-02T14:27:28.000Z","dependencies_parsed_at":"2023-11-06T12:41:41.473Z","dependency_job_id":"da2505d6-c32a-4126-8553-7fb4416fbf66","html_url":"https://github.com/leolabs/ableton-js","commit_stats":{"total_commits":443,"total_committers":10,"mean_commits":44.3,"dds":"0.038374717832957095","last_synced_commit":"065f068cbed8eb277bd5286c7d30ee2eda70f45d"},"previous_names":[],"tags_count":116,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leolabs%2Fableton-js","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leolabs%2Fableton-js/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leolabs%2Fableton-js/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leolabs%2Fableton-js/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/leolabs","download_url":"https://codeload.github.com/leolabs/ableton-js/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248830404,"owners_count":21168272,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ableton","api","javascript","js","live","ts","typescript"],"created_at":"2024-08-10T10:01:49.237Z","updated_at":"2025-04-14T06:03:09.624Z","avatar_url":"https://github.com/leolabs.png","language":"TypeScript","readme":"# Ableton.js\n\n[![Current Version](https://img.shields.io/npm/v/ableton-js.svg)](https://www.npmjs.com/package/ableton-js/)\n\nAbleton.js lets you control your instance or instances of Ableton using Node.js.\nIt tries to cover as many functions as possible.\n\nThis package is still a work-in-progress. My goal is to expose all of\n[Ableton's MIDI Remote Script](https://nsuspray.github.io/Live_API_Doc/11.0.0.xml)\nfunctions to TypeScript. If you'd like to contribute, please feel free to do so.\n\n## Sponsored Message\n\nI've used Ableton.js to build a setlist manager called\n[AbleSet](https://ableset.app). AbleSet allows you to easily manage and control\nyour Ableton setlists from any device, re-order songs and add notes to them, and\nget an overview of the current state of your set.\n\n[![AbleSet Header](https://public-files.gumroad.com/variants/oplxt68bsgq1hu61t8bydfkgppr5/baaca0eb0e33dc4f9d45910b8c86623f0144cea0fe0c2093c546d17d535752eb)](https://ableset.app/?utm_campaign=ableton-js)\n\n## Prerequisites\n\nTo use this library, you'll need to install and activate the MIDI Remote Script\nin Ableton.js. To do that, copy the `midi-script` folder of this repo to\nAbleton's Remote Scripts folder and rename it to `AbletonJS`. The MIDI Remote\nScripts folder is usually located at\n`~/Music/Ableton/User Library/Remote Scripts`\n\nAfter starting Ableton Live, add the script to your list of control surfaces:\n\n![Ableton Live Settings](https://i.imgur.com/a34zJca.png)\n\nIf you've forked this project on macOS, you can also use yarn to do that for\nyou. Running `yarn ableton10:start` or `yarn ableton11:start` (depending on your\napp version) will copy the `midi-script` folder, open Ableton and show a stream\nof log messages until you kill it.\n\n## Using Ableton.js\n\nThis library exposes an `Ableton` class which lets you control the entire\napplication. You can instantiate it once and use TS to explore available\nfeatures.\n\nExample:\n\n```typescript\nimport { Ableton } from \"ableton-js\";\n\n// Log all messages to the console\nconst ableton = new Ableton({ logger: console });\n\nconst test = async () =\u003e {\n  // Establishes a connection with Live\n  await ableton.start();\n\n  // Observe the current playback state and tempo\n  ableton.song.addListener(\"is_playing\", (p) =\u003e console.log(\"Playing:\", p));\n  ableton.song.addListener(\"tempo\", (t) =\u003e console.log(\"Tempo:\", t));\n\n  // Get the current tempo\n  const tempo = await ableton.song.get(\"tempo\");\n  console.log(\"Current tempo:\", tempo);\n\n  // Set the tempo\n  await ableton.song.set(\"tempo\", 85);\n};\n\ntest();\n```\n\n## Events\n\nThere are a few events you can use to get more under-the-hood insights:\n\n```ts\n// A connection to Ableton is established\nab.on(\"connect\", (e) =\u003e console.log(\"Connect\", e));\n\n// Connection to Ableton was lost,\n// also happens when you load a new project\nab.on(\"disconnect\", (e) =\u003e console.log(\"Disconnect\", e));\n\n// A raw message was received from Ableton\nab.on(\"message\", (m) =\u003e console.log(\"Message:\", m));\n\n// A received message could not be parsed\nab.on(\"error\", (e) =\u003e console.error(\"Error:\", e));\n\n// Fires on every response with the current ping\nab.on(\"ping\", (ping) =\u003e console.log(\"Ping:\", ping, \"ms\"));\n```\n\n## Protocol\n\nAbleton.js uses UDP to communicate with the MIDI Script. Each message is a JSON\nobject containing required data and a UUID so request and response can be\nassociated with each other.\n\n### Used Ports\n\nBoth the client and the server bind to a random available port and store that\nport in a local file so the other side knows which port to send messages to.\n\n### Compression and Chunking\n\nTo allow sending large JSON payloads, requests to and responses from the MIDI\nScript are compressed using gzip and chunked to fit into the maximum allowed\npackage size. The first byte of every message chunk contains the chunk index\n(0x00-0xFF) followed by the gzipped chunk. The last chunk always has the index\n0xFF. This indicates to the JS library that the previous received messages\nshould be stiched together, unzipped, and processed.\n\n### Caching\n\nCertain props are cached on the client to reduce the bandwidth over UDP. To do\nthis, the Ableton plugin generates an MD5 hash of the prop, called ETag, and\nsends it to the client along with the data.\n\nThe client stores both the ETag and the data in an LRU cache and sends the\nlatest stored ETag to the plugin the next time the same prop is requested. If\nthe data still matches the ETag, the plugin responds with a placeholder object\nand the client returns the cached data.\n\n### Commands\n\nA command payload consists of the following properties:\n\n```js\n{\n  \"uuid\": \"a20f25a0-83e2-11e9-bbe1-bd3a580ef903\", // A unique command id\n  \"ns\": \"song\", // The command namespace\n  \"nsid\": null, // The namespace id, for example to address a specific track or device\n  \"name\": \"get_prop\", // Command name\n  \"args\": { \"prop\": \"current_song_time\" }, // Command arguments\n  \"etag\": \"4e0794e44c7eb58bdbbbf7268e8237b4\", // MD5 hash of the data if it might be cached locally\n  \"cache\": true // If this is true, the plugin will calculate an etag and return a placeholder if it matches the provided one\n}\n```\n\nThe MIDI Script answers with a JSON object looking like this:\n\n```js\n{\n  \"data\": 0.0, // The command's return value, can be of any JSON-compatible type\n  \"event\": \"result\", // This can be 'result' or 'error'\n  \"uuid\": \"a20f25a0-83e2-11e9-bbe1-bd3a580ef903\" // The same UUID that was used to send the command\n}\n```\n\nIf you're getting a cached prop, the JSON object could look like this:\n\n```js\n{\n  \"data\": { \"data\": 0.0, \"etag\": \"4e0794e44c7eb58bdbbbf7268e8237b4\" },\n  \"event\": \"result\", // This can be 'result' or 'error'\n  \"uuid\": \"a20f25a0-83e2-11e9-bbe1-bd3a580ef903\" // The same UUID that was used to send the command\n}\n```\n\nOr, if the data hasn't changed, it looks like this:\n\n```js\n{\n  \"data\": { \"__cached\": true },\n  \"event\": \"result\", // This can be 'result' or 'error'\n  \"uuid\": \"a20f25a0-83e2-11e9-bbe1-bd3a580ef903\" // The same UUID that was used to send the command\n}\n```\n\n### Events\n\nTo attach an event listener to a specific property, the client sends a command\nobject:\n\n```js\n{\n  \"uuid\": \"922d54d0-83e3-11e9-ba7c-917478f8b91b\", // A unique command id\n  \"ns\": \"song\", // The command namespace\n  \"name\": \"add_listener\", // The command to add an event listener\n  \"args\": {\n    \"prop\": \"current_song_time\", // The property that should be watched\n    \"eventId\": \"922d2dc0-83e3-11e9-ba7c-917478f8b91b\" // A unique event id\n  }\n}\n```\n\nThe MIDI Script answers with a JSON object looking like this to confirm that the\nlistener has been attached:\n\n```js\n{\n  \"data\": \"922d2dc0-83e3-11e9-ba7c-917478f8b91b\", // The unique event id\n  \"event\": \"result\", // Should be result, is error when something goes wrong\n  \"uuid\": \"922d54d0-83e3-11e9-ba7c-917478f8b91b\" // The unique command id\n}\n```\n\nFrom now on, when the observed property changes, the MIDI Script sends an event\nobject:\n\n```js\n{\n  \"data\": 68.0, // The new value, can be any JSON-compatible type\n  \"event\": \"922d2dc0-83e3-11e9-ba7c-917478f8b91b\", // The event id\n  \"uuid\": null // Is always null and may be removed in future versions\n}\n```\n\nNote that for some values, this event is emitted multiple times per second.\n20-30 updates per second are not unusual.\n\n### Connection Events\n\nThe MIDI Script sends events when it starts and when it shuts down. These look\nlike this:\n\n```js\n{\n  \"data\": null, // Is always null\n  \"event\": \"connect\", // Can be connect or disconnect\n  \"uuid\": null // Is always null and may be removed in future versions\n}\n```\n\nWhen you open a new Project in Ableton, the script will shut down and start\nagain.\n\nWhen Ableton.js receives a disconnect event, it clears all current event\nlisteners and pending commands. It is usually a good idea to attach all event\nlisteners and get properties each time the `connect` event is emitted.\n\n### Findings\n\nIn this section, I'll note interesting pieces of information related to\nAbleton's Python framework that I stumble upon during the development of this\nlibrary.\n\n- It seems like Ableton's listener to `output_meter_level` doesn't quite work as\n  well as expected, hanging every few 100ms. Listening to `output_meter_left` or\n  `output_meter_right` works better. See\n  [Issue #4](https://github.com/leolabs/ableton-js/issues/4)\n- The `playing_status` listener of clip slots never fires in Ableton. See\n  [Issue #25](https://github.com/leolabs/ableton-js/issues/25)\n\n## Contributing\n\nIf you'd like to add features to this project or submit a bugfix, please feel\nfree to open a pull request. Before committing changes to any of the TypeScript\nfiles, please run `yarn format` to format the code using Prettier.\n","funding_links":["https://github.com/sponsors/leolabs","https://ko-fi.com/leolabs","https://paypal.me/leolabs"],"categories":["TypeScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleolabs%2Fableton-js","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fleolabs%2Fableton-js","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleolabs%2Fableton-js/lists"}