# Features

Producer Pal is an AI-powered music production assistant for Ableton Live. Tell
the AI what you want and it uses 20 specialized tools to read, create, and
modify tracks, clips, devices, and more in your Live Set.

It works with virtually any AI, including its
[built-in Chat UI](/guide/chat-ui), desktop apps like
[Claude Desktop](/installation/claude-desktop) and
[Codex](/installation/codex-app), CLI tools, and web apps.

[Get started →](/installation)

## Core Tools

### 🔧 Connect (`ppal-connect`) {#ppal-connect}

- Establish the connection with Ableton Live (required before using other tools)
- Summarizes the state of the current Live Set
- Returns a [skill set](#skills) that teaches the AI how to use Producer Pal
  effectively. Standard skills cover the full feature set.
  [Small model mode](#small-model-mode) provides simplified skills and schemas
  for less capable models.

<p class="vp-doc-muted">(no parameters)</p>

### 🔧 Context (`ppal-context`) {#ppal-context}

- Read and write project memory — persistent notes that help the AI understand
  your goals across conversations
- Search configured sample folder for audio files by filename or path

<details>
<summary>Parameters</summary>

| Parameter | Type | Description |
|-----------|------|-------------|
| `action` | `"read"`&nbsp;\|<br>`"write"`&nbsp;\|<br>`"search"`<br><span class="vp-doc-muted">(required)</span> | read: view memory \| write: update memory \| search: find audio samples |
| `content` | string | content to write (required for write) |
| `search` | string | case-insensitive substring filter (search only) |

</details>

## Transport Tools

### 🔧 Playback (`ppal-playback`) {#ppal-playback}

- Start/stop playback in Session or Arrangement view
- Play specific scenes or clips
- Set loop points and playback position
- Jump to arrangement locators by ID or name
- Set loop start/end using locators
- Control which tracks follow the Arrangement
- Stop all clips or specific track clips

<details>
<summary>Parameters</summary>

| Parameter | Type | Description |
|-----------|------|-------------|
| `action` | `"play-arrangement"`&nbsp;\|<br>`"update-arrangement"`&nbsp;\|<br>`"play-scene"`&nbsp;\|<br>`"play-session-clips"`&nbsp;\|<br>`"stop-session-clips"`&nbsp;\|<br>`"stop-all-session-clips"`&nbsp;\|<br>`"stop"`<br><span class="vp-doc-muted">(required)</span> | play-arrangement: from startTime<br>update-arrangement: modify loop<br>play-scene: all clips in scene<br>play-session-clips: by id(s) or slot(s)<br>stop-session-clips: by id(s) or slot(s)<br>stop-all-session-clips: all<br>stop: session and arrangement |
| `startTime` | string | bar\|beat position in arrangement |
| `startLocator` 🐘 | string | locator ID or name for start position (e.g., locator-0 or Verse) |
| `loop` | boolean | arrangement loop? |
| `loopStart` | string | bar\|beat position |
| `loopStartLocator` 🐘 | string | locator ID or name for loop start |
| `loopEnd` | string | bar\|beat position |
| `loopEndLocator` 🐘 | string | locator ID or name for loop end |
| `ids` | string | comma-separated ID(s) for clip operations |
| `slots` | string | session clip slot(s), trackIndex/sceneIndex format, comma-separated (e.g., '0/1' or '0/1,2/3') |
| `sceneIndex` | integer <nobr><span class="vp-doc-muted">(≥ 0)</span></nobr> | 0-based scene index for play-scene |

_🐘 = large model only (hidden in small model mode)_

</details>

## Live Set Tools

### 🔧 Read Live Set (`ppal-read-live-set`) {#ppal-read-live-set}

- Get complete Live project overview
- View all tracks, scenes, and clips at once
- See tempo, time signature, and scale settings
- View arrangement locators with times and names
- Check what's playing and track states

<details>
<summary>Parameters</summary>

| Parameter | Type | Description |
|-----------|------|-------------|
| `include` | <span class="vp-doc-muted">array of:</span><br>`"tracks"`&nbsp;\|<br>`"scenes"`&nbsp;\|<br>`"routings"`&nbsp;\|<br>`"mixer"`&nbsp;\|<br>`"color"`&nbsp;\|<br>`"locators"`&nbsp;🐘&nbsp;\|<br>`"*"`&nbsp;🐘 | tracks, scenes = lists. routings, mixer, color = detail (use with tracks/scenes). locators = arrangement markers. "*" = all |

_🐘 = large model only (hidden in small model mode)_

</details>

### 🔧 Update Live Set (`ppal-update-live-set`) {#ppal-update-live-set}

- Change tempo, time signature, scale
- Create, rename, or delete arrangement locators

<details>
<summary>Parameters</summary>

| Parameter | Type | Description |
|-----------|------|-------------|
| `tempo` | number <nobr><span class="vp-doc-muted">(20–999)</span></nobr> | BPM |
| `timeSignature` | string | N/D (4/4) |
| `scale` | string | "Root ScaleName" ("C Major", "F# Minor", "Bb Dorian"). Empty string disables scale |
| `locatorOperation` 🐘 | `"create"`&nbsp;\|<br>`"delete"`&nbsp;\|<br>`"rename"` | Locator operation |
| `locatorId` 🐘 | string | Locator ID for delete/rename (e.g., locator-0) |
| `locatorTime` 🐘 | string | Bar\|beat position (required for create, alt ID for delete/rename) |
| `locatorName` 🐘 | string | Name for create/rename, or name-match filter for delete |

_🐘 = large model only (hidden in small model mode)_

</details>

## Track Tools

### 🔧 Create Track (`ppal-create-track`) {#ppal-create-track}

- Add MIDI, audio, or return tracks
- Position tracks exactly where you want
- Set initial mute/solo/arm states

<details>
<summary>Parameters</summary>

| Parameter | Type | Description |
|-----------|------|-------------|
| `trackIndex` | integer | 0-based index, -1 or omit to append |
| `count` 🐘 | integer <nobr><span class="vp-doc-muted">(≥ 1)</span></nobr> | number to create |
| `name` | string | name for all, or comma-separated for each |
| `color` | string | #RRGGBB for all, or comma-separated for each (cycles if fewer than count) |
| `type` | `"midi"`&nbsp;\|<br>`"audio"`&nbsp;\|<br>`"return"` | type |
| `mute` 🐘 | boolean | muted? |
| `solo` 🐘 | boolean | soloed? |
| `arm` 🐘 | boolean | record armed? |

_🐘 = large model only (hidden in small model mode)_

</details>

### 🔧 Read Track (`ppal-read-track`) {#ppal-read-track}

- Get detailed track information
- View all clips in Session and Arrangement
- See devices, routing options, and drum pad mappings
- Check track states (muted, soloed, armed)
- View mixer properties: gain, pan, panning mode, and send levels

<details>
<summary>Parameters</summary>

| Parameter | Type | Description |
|-----------|------|-------------|
| `trackId` | string | provide this or trackType/trackIndex |
| `trackType` | `"return"`&nbsp;\|<br>`"master"` | return or master (omit for audio/midi tracks, which have independent trackIndexes) |
| `trackIndex` | integer <nobr><span class="vp-doc-muted">(≥ 0)</span></nobr> | 0-based index |
| `include` | <span class="vp-doc-muted">array of:</span><br>`"session-clips"`&nbsp;\|<br>`"arrangement-clips"`&nbsp;\|<br>`"notes"`&nbsp;\|<br>`"timing"`&nbsp;\|<br>`"sample"`&nbsp;\|<br>`"devices"`&nbsp;\|<br>`"drum-map"`&nbsp;\|<br>`"routings"`&nbsp;\|<br>`"available-routings"`&nbsp;🐘&nbsp;\|<br>`"mixer"`&nbsp;\|<br>`"color"`&nbsp;\|<br>`"*"`&nbsp;🐘 | session-clips, arrangement-clips = clip lists. notes, timing, sample = clip detail (use with clips). devices, drum-map, routings, available-routings, mixer = track data. color = track + clip color. "*" = all |

_🐘 = large model only (hidden in small model mode)_

</details>

### 🔧 Update Track (`ppal-update-track`) {#ppal-update-track}

- Change track gain (volume), panning, and send levels
- Change mute, solo, arm, I/O routings, and monitoring state
- Change track name and color
- Update multiple tracks at once

<details>
<summary>Parameters</summary>

| Parameter | Type | Description |
|-----------|------|-------------|
| `ids` | string <nobr><span class="vp-doc-muted">(required)</span></nobr> | comma-separated track ID(s) to update |
| `name` | string | name for all, or comma-separated for each (extras keep existing name), ideally unique |
| `color` | string | #RRGGBB for all, or comma-separated for each (cycles if fewer than ids) |
| `gainDb` | number <nobr><span class="vp-doc-muted">(-70–6)</span></nobr> | track gain in dB |
| `pan` | number <nobr><span class="vp-doc-muted">(-1–1)</span></nobr> | pan: -1 (left) to 1 (right) |
| `panningMode` 🐘 | `"stereo"`&nbsp;\|<br>`"split"` | panning mode: stereo or split |
| `leftPan` 🐘 | number <nobr><span class="vp-doc-muted">(-1–1)</span></nobr> | left channel pan in split mode (-1 to 1) |
| `rightPan` 🐘 | number <nobr><span class="vp-doc-muted">(-1–1)</span></nobr> | right channel pan in split mode (-1 to 1) |
| `mute` | boolean | muted? |
| `solo` | boolean | soloed? |
| `arm` | boolean | record armed? |
| `inputRoutingTypeId` 🐘 | string | from availableInputRoutingTypes, set before channel |
| `inputRoutingChannelId` 🐘 | string | from availableInputRoutingChannels |
| `outputRoutingTypeId` 🐘 | string | from availableOutputRoutingTypes, set before channel |
| `outputRoutingChannelId` 🐘 | string | from availableOutputRoutingChannels |
| `monitoringState` 🐘 | `"in"`&nbsp;\|<br>`"auto"`&nbsp;\|<br>`"off"` | input monitoring |
| `sendGainDb` 🐘 | number <nobr><span class="vp-doc-muted">(-70–0)</span></nobr> | send gain in dB, requires sendReturn |
| `sendReturn` 🐘 | string | return track: exact name (e.g., "A-Reverb") or letter (e.g., "A") |

_🐘 = large model only (hidden in small model mode)_

</details>

## Scene Tools

### 🔧 Create Scene (`ppal-create-scene`) {#ppal-create-scene}

- Add new scenes at any position
- Set scene name, color, tempo, and time signature
- Scenes can follow song tempo or have their own
- Ability to capture currently playing clips into a new scene

<details>
<summary>Parameters</summary>

| Parameter | Type | Description |
|-----------|------|-------------|
| `sceneIndex` | integer <nobr><span class="vp-doc-muted">(≥ 0)</span></nobr> | 0-based index for new scene(s), shifts existing scenes. Required when capture=false, optional when capture=true |
| `count` 🐘 | integer <nobr><span class="vp-doc-muted">(≥ 1)</span></nobr> | number to create |
| `capture` 🐘 | boolean | copy playing session clips instead of creating empty? |
| `name` | string | name for all, or comma-separated for each |
| `color` | string | #RRGGBB for all, or comma-separated for each (cycles if fewer than count) |
| `tempo` 🐘 | number | BPM (-1 disables when capturing) |
| `timeSignature` 🐘 | string | N/D (4/4) or "disabled" when capturing |

_🐘 = large model only (hidden in small model mode)_

</details>

### 🔧 Read Scene (`ppal-read-scene`) {#ppal-read-scene}

- View scene details and all its clips
- Check which clips are playing/triggered
- See scene tempo and time signature

<details>
<summary>Parameters</summary>

| Parameter | Type | Description |
|-----------|------|-------------|
| `sceneId` | string | provide this or sceneIndex |
| `sceneIndex` | integer <nobr><span class="vp-doc-muted">(≥ 0)</span></nobr> | 0-based index |
| `include` | <span class="vp-doc-muted">array of:</span><br>`"clips"`&nbsp;\|<br>`"notes"`&nbsp;\|<br>`"sample"`&nbsp;\|<br>`"timing"`&nbsp;\|<br>`"color"`&nbsp;\|<br>`"*"` | clips = clip list. notes, sample, timing = clip detail (use with clips). color = scene + clip color. "*" = all |

</details>

### 🔧 Update Scene (`ppal-update-scene`) {#ppal-update-scene}

- Change scene name, color, tempo, and time signature
- Update multiple scenes at once

<details>
<summary>Parameters</summary>

| Parameter | Type | Description |
|-----------|------|-------------|
| `ids` | string <nobr><span class="vp-doc-muted">(required)</span></nobr> | comma-separated scene ID(s) to update |
| `name` | string | name for all, or comma-separated for each (extras keep existing name) |
| `color` | string | #RRGGBB for all, or comma-separated for each (cycles if fewer than ids) |
| `tempo` | number | BPM (-1 disables) |
| `timeSignature` | string | N/D (4/4) or "disabled" |

</details>

## Device Tools

### 🔧 Create Device (`ppal-create-device`) {#ppal-create-device}

- Add native Live devices (instruments, MIDI effects, audio effects)
- Place devices on any track type: MIDI, audio, return, or master
- Position devices at a specific index in the device chain
- Create devices inside rack chains or drum pads using path notation
- List the native Live devices

<details>
<summary>Parameters</summary>

| Parameter | Type | Description |
|-----------|------|-------------|
| `deviceName` | string | device name, omit to list available devices |
| `path` | string | insertion path(s), required with deviceName, comma-separated for multiple (e.g., 't0' or 't0,t1,t0/d0/c0') |
| `name` | string | name for all, or comma-separated for each |

</details>

### 🔧 Read Device (`ppal-read-device`) {#ppal-read-device}

- Get detailed info about any device, including inside rack chains and drum pad
  chains
- List device parameter names and values (the state of knobs, dials, etc)

<details>
<summary>Parameters</summary>

| Parameter | Type | Description |
|-----------|------|-------------|
| `deviceId` | string | Device ID to read |
| `path` | string | path (e.g., 't1/d0', 't1/d0/c0', 't1/d0/pC1', 't1/d0/rc0') |
| `include` | <span class="vp-doc-muted">array of:</span><br>`"chains"`&nbsp;\|<br>`"drum-map"`&nbsp;\|<br>`"drum-pads"`&nbsp;🐘&nbsp;\|<br>`"params"`&nbsp;\|<br>`"param-values"`&nbsp;\|<br>`"return-chains"`&nbsp;🐘&nbsp;\|<br>`"sample"`&nbsp;\|<br>`"*"`&nbsp;🐘 | chains, return-chains, drum-pads = rack contents (use maxDepth). params, param-values = parameters. drum-map = note names. sample = Simpler file. "*" = all |
| `maxDepth` | integer <nobr><span class="vp-doc-muted">(≥ 0)</span></nobr> | Device tree depth for chains/drum-pads. 0=chains only with deviceCount, 1=direct devices, 2+=deeper |
| `paramSearch` | string | Filter parameters by case-insensitive substring match on name |

_🐘 = large model only (hidden in small model mode)_

</details>

### 🔧 Update Device (`ppal-update-device`) {#ppal-update-device}

- Change device name
- Change device parameter values (control knobs, dials, etc)
- Update multiple devices at once
- Move devices anywhere else in the Live Set, including into racks / wrapping in
  a new rack
- Create, load, delete, and randomize rack macros variations
- A/B Compare with supported devices
- Control chain and drum pad mute and solo state
- Change the choke group and output MIDI note of drum chains

<details>
<summary>Parameters</summary>

| Parameter | Type | Description |
|-----------|------|-------------|
| `ids` | string | comma-separated ID(s) to update (device, chain, or drum pad) |
| `path` | string | comma-separated path(s) (e.g., 't1/d0', 't1/d0/c0', 't1/d0/pC1') |
| `toPath` | string | move to path (e.g., 't2', 't0/d0/c1', 't0/d0/pD1') |
| `name` | string | name for all, or comma-separated for each (extras keep existing name, not drum pads) |
| `params` | string | name=value per line (display units: enum string, note name, number) |
| `macroVariation` 🐘 | `"create"`&nbsp;\|<br>`"load"`&nbsp;\|<br>`"delete"`&nbsp;\|<br>`"revert"`&nbsp;\|<br>`"randomize"` | Rack only: create/load/delete/revert variation, or randomize macros. load/delete require macroVariationIndex. create always appends. |
| `macroVariationIndex` 🐘 | integer <nobr><span class="vp-doc-muted">(≥ 0)</span></nobr> | Rack only: variation index for load/delete operations (0-based) |
| `macroCount` 🐘 | integer <nobr><span class="vp-doc-muted">(0–16)</span></nobr> | Rack only: set visible macro count (0-16) |
| `abCompare` 🐘 | `"a"`&nbsp;\|<br>`"b"`&nbsp;\|<br>`"save"` | AB Compare: switch to 'a' or 'b' preset, or 'save' current to other slot |
| `mute` | boolean | mute state (chains/drum pads only) |
| `solo` | boolean | solo state (chains/drum pads only) |
| `color` | string | #RRGGBB for all, or comma-separated for each (cycles if fewer than ids; chains only) |
| `chokeGroup` 🐘 | integer <nobr><span class="vp-doc-muted">(0–16)</span></nobr> | choke group 0-16, 0=none (drum chains only) |
| `mappedPitch` 🐘 | string | output MIDI note e.g. 'C3' (drum chains only) |
| `wrapInRack` 🐘 | boolean | Wrap device(s) in a new rack (auto-detects type from device) |

_🐘 = large model only (hidden in small model mode)_

</details>

## Clip Tools

### 🔧 Create Clip (`ppal-create-clip`) {#ppal-create-clip}

- Generate MIDI clips with notes, velocities, and timing using
  [custom notation](#custom-music-notation)
- Place clips in Session slots or Arrangement timeline
- Support for probability, velocity ranges, and complex rhythms
- Apply [transforms](#transforms) to shape notes with math expressions
- Auto-create scenes as needed

<details>
<summary>Parameters</summary>

| Parameter | Type | Description |
|-----------|------|-------------|
| `slot` | string | session clip slot(s): trackIndex/sceneIndex, comma-separated (e.g., '0/0' or '0/0,0/2,0/5') |
| `trackIndex` | integer <nobr><span class="vp-doc-muted">(≥ 0)</span></nobr> | 0-based track index (arrangement clips) |
| `arrangementStart` | string | arrangement clip bar\|beat position(s), comma-separated for multiple (e.g., '1\|1' or '1\|1,2\|1,3\|3') |
| `name` | string | name for all, or comma-separated for each (indexed: session positions first, then arrangement) |
| `color` | string | #RRGGBB for all, or comma-separated for each (cycles if fewer than positions) |
| `timeSignature` | string | N/D (4/4), default: global time signature |
| `start` | string | bar\|beat position where loop/clip region begins |
| `length` | string | duration in bar:beat (e.g., '4:0' = 4 bars), default: next full bar after latest note |
| `looping` | boolean | enable looping for the clip |
| `firstStart` 🐘 | string | bar\|beat playback start (looping clips, when different from start) |
| `notes` | string | MIDI in bar\|beat notation: [bar\|beat] [v0-127] [t&lt;dur&gt;] [p0-1] note(s) - MIDI clips only |
| `transforms` 🐘 | string | transform expressions (parameter: expression per line) |
| `sampleFile` | string | absolute path to audio file - audio clips only |
| `auto` 🐘 | `"play-scene"`&nbsp;\|<br>`"play-clip"` | auto-play session clips (play-scene keeps scene in sync) |

_🐘 = large model only (hidden in small model mode)_

</details>

### 🔧 Read Clip (`ppal-read-clip`) {#ppal-read-clip}

- Get detailed info about any clip in Session or Arrangement
- Read MIDI notes in [custom notation](#custom-music-notation) (C3, D#4, etc.)
- Get audio clip gain, pitch, warp settings, and sample info

<details>
<summary>Parameters</summary>

| Parameter | Type | Description |
|-----------|------|-------------|
| `clipId` | string | provide this or slot |
| `slot` | string | session clip slot: trackIndex/sceneIndex (e.g., '0/3'). provide this or clipId |
| `include` | <span class="vp-doc-muted">array of:</span><br>`"sample"`&nbsp;\|<br>`"notes"`&nbsp;\|<br>`"color"`&nbsp;\|<br>`"timing"`&nbsp;\|<br>`"warp"`&nbsp;🐘&nbsp;\|<br>`"*"`&nbsp;🐘 | notes = MIDI data. timing = loop/start/end markers. sample = audio file info. warp = warp settings. color. "*" = all |

_🐘 = large model only (hidden in small model mode)_

</details>

### 🔧 Update Clip (`ppal-update-clip`) {#ppal-update-clip}

- Change clip name, color, and loop settings
- Add/remove MIDI notes using [custom notation](#custom-music-notation)
- Apply [transforms](#transforms) to modify existing notes and audio properties
- Change audio clip gain, pitch shift, and warp settings
- Move clips and change their length in the Arrangement
- Split arrangement clips at specified positions
- Update multiple clips at once

<details>
<summary>Parameters</summary>

| Parameter | Type | Description |
|-----------|------|-------------|
| `ids` | string <nobr><span class="vp-doc-muted">(required)</span></nobr> | comma-separated clip ID(s) to update |
| `name` | string | name for all, or comma-separated for each (extras keep existing name) |
| `color` | string | #RRGGBB for all, or comma-separated for each (cycles if fewer than ids) |
| `timeSignature` | string | N/D (4/4) |
| `start` | string | bar\|beat position where loop/clip region begins |
| `length` | string | duration in bar:beat (e.g., '4:0' = 4 bars) |
| `looping` | boolean | enable looping for the clip |
| `firstStart` 🐘 | string | bar\|beat playback start (looping clips, when different from start) |
| `arrangementStart` | string | bar\|beat position to move arrangement clip (arrangement clips only) |
| `arrangementLength` | string | duration in bar:beat (e.g., '4:0' = 4 bars), arrangement clips only |
| `toSlot` | string | trackIndex/sceneIndex to move session clip (e.g., '2/3') |
| `split` 🐘 | string | comma-separated bar\|beat positions to split clip (e.g., '2\|1, 3\|1') - max 32 points, arrangement clips only |
| `gainDb` | number <nobr><span class="vp-doc-muted">(-70–24)</span></nobr> | audio clip gain in decibels (ignored for MIDI) |
| `pitchShift` | number <nobr><span class="vp-doc-muted">(-48–48)</span></nobr> | audio clip pitch shift in semitones, supports decimals (ignored for MIDI) |
| `warpMode` | `"beats"`&nbsp;\|<br>`"tones"`&nbsp;\|<br>`"texture"`&nbsp;\|<br>`"repitch"`&nbsp;\|<br>`"complex"`&nbsp;\|<br>`"pro"` | audio clip warp mode (ignored for MIDI) |
| `warping` | boolean | audio clip warping on/off (ignored for MIDI) |
| `notes` | string | MIDI notes in bar\|beat notation: [bar\|beat] [v0-127] [t&lt;dur&gt;] [p0-1] note(s) - MIDI clips only |
| `transforms` 🐘 | string | transform expressions (parameter: expression per line) |
| `noteUpdateMode` | `"replace"`&nbsp;\|<br>`"merge"` | "replace" (clear all notes first) or "merge" (overlay notes, v0 deletes) |
| `quantize` | number <nobr><span class="vp-doc-muted">(0–1)</span></nobr> | quantization strength 0-1 (MIDI clips only) |
| `quantizeGrid` | `"1/4"`&nbsp;\|<br>`"1/8"`&nbsp;\|<br>`"1/8T"`&nbsp;\|<br>`"1/8+1/8T"`&nbsp;\|<br>`"1/16"`&nbsp;\|<br>`"1/16T"`&nbsp;\|<br>`"1/16+1/16T"`&nbsp;\|<br>`"1/32"` | note grid (required with quantize) |
| `quantizePitch` 🐘 | string | limit quantization to specific pitch (e.g., C3, D#4) |

_🐘 = large model only (hidden in small model mode)_

</details>

## Action Tools

### 🔧 Delete (`ppal-delete`) {#ppal-delete}

- Remove tracks, return tracks, scenes, clips, or devices
- Bulk delete multiple objects

<details>
<summary>Parameters</summary>

| Parameter | Type | Description |
|-----------|------|-------------|
| `ids` | string | comma-separated ID(s) to delete (must be same type) |
| `path` | string | comma-separated device/drum-pad paths to delete (e.g., 't0/d1', 't1/d0/pC1/d0', 't1/d0/pC1') |
| `type` | `"track"`&nbsp;\|<br>`"scene"`&nbsp;\|<br>`"clip"`&nbsp;\|<br>`"device"`&nbsp;\|<br>`"drum-pad"`<br><span class="vp-doc-muted">(required)</span> | type of objects to delete |

</details>

### 🔧 Duplicate (`ppal-duplicate`) {#ppal-duplicate}

- Copy tracks, scenes, clips, or devices
- Create multiple copies at once
- Copy clips anywhere in the Session, Arrangement, or from Session to
  Arrangement
  - Position in the Arrangement by bar|beat or locator
  - Auto-tile clips to fill longer arrangement durations
- Copy devices to any track, return track, or rack chain
- Route duplicated tracks to source instrument for MIDI layering

Note: Return tracks and devices on return tracks cannot be duplicated (Live API
limitation).

<details>
<summary>Parameters</summary>

| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | string <nobr><span class="vp-doc-muted">(required)</span></nobr> | object to duplicate |
| `type` | `"track"`&nbsp;\|<br>`"scene"`&nbsp;\|<br>`"clip"`&nbsp;\|<br>`"device"`<br><span class="vp-doc-muted">(required)</span> | type of object to duplicate |
| `name` | string | name (comma-separated when duplicating multiple) |
| `color` | string | #RRGGBB (comma-separated when duplicating multiple, cycles) |
| `count` 🐘 | integer <nobr><span class="vp-doc-muted">(≥ 1)</span></nobr> | number of copies (tracks/scenes only, ignored for clips/devices) |
| `withoutClips` 🐘 | boolean | exclude clips? |
| `withoutDevices` 🐘 | boolean | exclude devices? |
| `arrangementStart` | string | arrangement bar\|beat position(s) for clips/scenes, comma-separated for multiple (e.g., '1\|1' or '1\|1,2\|1,3\|1') |
| `locator` 🐘 | string | arrangement locator ID(s) or name(s), comma-separated for multiple (e.g., 'locator-0' or 'Verse' or 'locator-0,Chorus') |
| `arrangementLength` | string | duration in bar:beat (e.g., '4:0' = 4 bars), auto-fills with loops |
| `toSlot` | string | session destination clip slot(s), trackIndex/sceneIndex format, comma-separated for multiple (e.g., '0/1' or '0/1,2/3') |
| `toPath` | string | device destination path(s), comma-separated for multiple (e.g., 't1/d0' or 't1/d0,t2/d0') |
| `routeToSource` 🐘 | boolean | route new track to source's instrument? (for MIDI layering/polyrhythms) |

_🐘 = large model only (hidden in small model mode)_

</details>

### 🔧 Select (`ppal-select`) {#ppal-select}

- Read current selection and view state (when no arguments)
  - Returns only non-null fields: selected track, scene, clip, device
  - Rich object shapes with IDs, types, and context (slot, path, etc.)
- Update selection and returns only relevant fields
  - Select any object by ID (auto-detects track/scene/clip/device)
  - Select tracks by index/category, scenes by index
  - Select clips by slot position (e.g., `0/3`)
  - Select devices by path (e.g., `t0/d1`)
  - Switch between Session and Arrangement views
  - Auto-switches to session view for scene/clipSlot selection
  - Detail views auto-managed: clip detail opens on clip selection, device
    detail on device selection

<details>
<summary>Parameters</summary>

| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | string | select by ID (auto-detects track/scene/clip/device) |
| `trackIndex` | integer <nobr><span class="vp-doc-muted">(≥ 0)</span></nobr> | 0-based track index |
| `trackType` | `"return"`&nbsp;\|<br>`"master"` | omit for audio/midi tracks, or: return, master |
| `sceneIndex` | integer <nobr><span class="vp-doc-muted">(≥ 0)</span></nobr> | 0-based scene index |
| `slot` | string | session clip slot: trackIndex/sceneIndex (e.g., '0/3') |
| `devicePath` | string | select device by path (e.g. t0/d1) |
| `view` | `"session"`&nbsp;\|<br>`"arrangement"` | main view |

</details>

## Custom Music Notation {#custom-music-notation}

Producer Pal uses a text-based music notation syntax called `bar|beat` to work
with MIDI clips. Used by [Create Clip](#ppal-create-clip),
[Update Clip](#ppal-update-clip), and [Read Clip](#ppal-read-clip). It helps
LLMs translate natural language expressions of time to the correct time
positions in Ableton Live clips and the arrangement timeline.

- **Pitches**: Standard notation (C3 = middle C, F#4, Bb2, etc.)
- **Time positions**: bar|beat format (1|1 = first beat, 2|3 = bar 2, beat 3)
- **Durations**: bar:beat format (4:0 = 4 bars, 1:2 = 1 bar + 2 beats)
- **Velocity**: Values from 1-127 (or ranges like 80-100)
- **Probability**: 0.0 to 1.0 (1.0 = always plays)
- **Bar copying**: Copy bars with `@2=1` (bar 1→2), ranges with `@2-8=1` (bar
  1→bars 2-8), or tile patterns with `@3-10=1-2` (repeat 2-bar pattern across
  bars 3-10)

## Transforms {#transforms}

Apply complex changes to clips using math expressions via
[Create Clip](#ppal-create-clip) and [Update Clip](#ppal-update-clip):

- **Transform MIDI notes**: velocity, pitch, timing, duration, probability
- **Transform audio clips**: gain, pitch shift
- **Shapes**: LFO waveforms (sine, tri, saw), ramps, curves, randomization with
  arbitrary ranges, choose from sets of values (e.g. chord notes)
- **Context variables**: Access note order (`note.index`), clip metadata
  (`clip.duration`, `clip.index`, `clip.position`, `clip.barDuration`) in
  expressions
- **Selectors**: Target specific pitch ranges (e.g., `C3:`, `C3-C5:`) or time
  ranges (e.g., `1|1-2|4:`), or both in either order (e.g., `C3 1|1-2|4:` or
  `1|1-2|4 C3:`)

## Network Control

Control Ableton Live from another computer on your local network, no extra setup
required. For fully remote control, use
[web tunnels](/installation/web-tunnels).

## Small Model Mode {#small-model-mode}

Adapts Producer Pal for less capable AI models by returning simplified
[skills](#skills) and removing advanced parameters from tool schemas. This is an
ongoing R&D effort aimed at making [local models](/installation/choose-local)
viable for completely offline, free, and private usage. Enable it in the
[Chat UI](/guide/chat-ui) settings or with `--small-model-mode` on the command
line.

## Skills {#skills}

The [Connect tool](#ppal-connect) returns a skill set that teaches the AI how to
use Producer Pal's [custom notation](#custom-music-notation),
[transforms](#transforms), device paths, and other conventions. Two variants are
available depending on [small model mode](#small-model-mode):

::: details Standard Skills

#### Time in Ableton Live

- Positions: bar|beat (1-indexed). Examples: 1|1, 2|3.5, 1|2+1/3
- Durations: beats (2.5, 3/4, /4) or bar:beat (1:2, 4:0)
- Fractional beats: decimals (2.5), fractions (5/2), mixed (2+1/3). Numerator defaults to 1 (/4 = 1/4)

#### MIDI Syntax

Create MIDI clips using the bar|beat notation syntax:

`[v0-127] [t<duration>] [p0-1] note(s) bar|beat`

- Parameters (v/t/p), pitches, and positions can appear in any order and be interspersed
- Notes emit at time positions (bar|beat)
  - time positions are relative to clip start
  - the beat in bar|beat can be a comma-separated (no whitespace) list or repeat pattern
  - **Repeat patterns**: `{bar|beat}x{count}[@{step}]` generates sequences. count = how many notes
    - step (in beats) defaults to duration (legato). step > duration = gaps; step < duration = overlap
    - `1|1x4@1` → beats 1,2,3,4; `t0.5 1|1x4` → 1, 1.5, 2, 2.5 (step defaults to t value)
    - `1|1x3@/3` → triplets; `t/4 1|1x16` → 16 notes at 16th-note spacing (x16 = count, t/4 = spacing)
- v&lt;velocity&gt;: 0-127 (default: v100). Range v80-120 randomizes per note for humanization
  - `v0` deletes earlier notes at same pitch/time (**deletes until disabled** with non-zero v)
- t&lt;duration&gt;: Note length (default: 1.0). Beats: t2.5, t3/4, t/4. Bar:beat: t2:1.5, t1:/4
- p&lt;chance&gt;: Probability from 0.0 to 1.0 (default: 1.0 = always)
- Notes: C0-G8 with # or b for sharps/flats (C#3, Bb2). C3 = middle C
- **Stateful**: v/t/p and pitch persist until changed — set once, applies to all following notes
- copying bars (**MERGES** - use v0 to clear unwanted notes):
  - @N= copies previous bar; @N=M copies bar M to N; @N-M=P copies bar P to range
  - @N-M=P-Q tiles bars P-Q across range; @clear clears copy buffer
  - Copies capture each note's v/t/p at the time it was written, not the current state
- **update-clip** `noteUpdateMode`: "merge" (default, overlay + v0 deletes) or "replace" (clear all existing notes first)

#### Audio Clips
`ppal-read-clip` `sample` include: `sampleFile`, `gainDb` (dB, 0=unity), `pitchShift` (semitones). `warp` include: `sampleLength`, `sampleRate`, `warping`, `warpMode`.
Audio params ignored when updating MIDI clips.

#### Examples

```
C#3 F3 G#3 1|1 // chord at bar 1 beat 1
C3 E3 G3 1|1,2,3,4 // same chord on every beat
C1 1|1,3 2|1,2,3 // same pitch across bars (NOT 1|1,3,2|1,2,3)
t0.25 C3 1|1.75 // 16th note at beat 1.75
t1/3 C3 1|1x3 // triplets: 3 notes across 1 beat (step = duration)
t/4 Gb1 1|1x16 // full bar of 16th note hi-hats
C3 D3 1|1 v0 C3 1|1 // delete earlier C3 (D3 remains)
C3 D3 1|1 @2=1 v0 D3 2|1 // bar copy then delete D3 from bar 2
v90-110 C1 1|1,3 D1 1|2,4 // humanized drum pattern
p0.5 C1 1|1,2,3,4 // 50% chance each kick plays
```

#### Techniques

Complete bars before copying. Use beat lists for irregular patterns.

```
C1 1|1,3 D1 1|2,4 // bar 1
@2-3=1            // bar 1 -> 2,3
C1 4|1,3.5 D1 4|4 // bar 4
@5-7=1            // bar 1 -> 5,6,7
@8=4              // bar 4 -> 8
```

##### Repeats with Variations

Copy foundation to **all bars** (including variation bars), then modify:

```
C1 1|1,3 D1 1|2,4       // bar 1 foundation
Gb1 1|1.5,2.5,3.5,4.5
@2-16=1                 // copy to ALL bars, not just 2-8
v0 Gb1 9|4.5 v100       // remove hat from bar 9
C1 9|3.5                // add extra kick to bar 9
v0 C1 13|3 v100 D1 13|3 // replace kick with snare in bar 13
```

##### Transforms

Add `transforms` parameter to create-clip or update-clip.

**Syntax:** `[selector:] parameter operator expression` (one per line)
- **Selector:** pitch and/or time filter, followed by `:` - e.g., `C3:`, `1|1-2|4:`, `C3 1|1-2|4:`, `1|1-2|4 C3:`
- **Pitch filter:** `C3` (single) or `C3-C5` (range) - omit for all pitches
- **Time filter:** `1|1-2|4` (bar|beat range, inclusive, matches note start time)
- **MIDI parameters:** velocity (1-127; <=0 deletes note), pitch (0-127), timing (beats), duration (beats; <=0 deletes note), probability (0-1), deviation (-127 to 127)
- **Audio parameters:** gain (-70 to 24 dB), pitchShift (-48 to 48 semitones)
- **Operators:** `+=`, `-=` (add/subtract), `*=`, `/=` (scale current value), `=` (set)
- **Expression:** arithmetic (+, -, *, /, %) with numbers, waveforms, math functions, and current values
- **Math functions:** round(x), floor(x), ceil(x), abs(x), clamp(val,min,max), wrap(val,min,max) (wrap to inclusive range), reflect(val,min,max) (bounce within inclusive range), min(a,b,...), max(a,b,...), pow(base,exp), snap(pitch) (snap to Live Set scale; no-op if no scale), step(pitch, offset) (move by offset scale steps; even distribution for waveforms), legato([tolerance]) (set duration to reach next note's start time; optional tolerance in beats groups nearby starts as chords, e.g. legato(0.1) after humanizing)
- **Timing functions:** swing(amount [, grid] [, raw]) (auto-quantizes to grid then applies swing; amount=delay in beats: 0.02=subtle, 0.05=medium, 0.1=heavy; grid: default 1/2t=8th-note swing, 1/4t=16th-note swing; raw: skip auto-quantize), quant(grid) (snap to nearest grid point). Grid ref for both: 1t=quarter, 1/2t=8th, 1/4t=16th, 1/3t=triplet. Both return absolute positions — use `timing =`, not `timing +=`

**Waveforms** (-1.0 to 1.0, per note position; once for audio):
- `cos(period)`, `square(period)` - start at peak (1.0); `sin(period)`, `tri(period)`, `saw(period)` - start at zero, rise to peak
  - All accept optional phase offset: `cos(1t, 0.25)`. square adds pulse width (3rd arg): `square(1t, 0, 0.75)` (phase=0, 75% duty cycle)
- `rand([min], [max])` - random value (no args: -1 to 1, one arg: 0 to max, two: min to max)
- `seq(a, b, ...)` - cycle through values by note.index (MIDI) or clip.index (audio)
- `choose(a, b, ...)` - random selection from arguments
- `ramp(start, end)` - linear interpolation; reaches end value at time range end (or clip end)
- `curve(start, end, exp)` - exponential (exp>1: slow start, exp<1: fast start); reaches end value at time range end
- For ramp/curve, end the time filter on the last note's beat position so it reaches its end value. In 4/4: last 8th=N|4.5, last 16th=N|4.75
- Waveform period: `1t` = 1 beat cycle, `1:0t` = 1 bar cycle, `0:2t` = 2 beat cycle
- `sync` keyword (last arg on periodic waves) syncs phase to arrangement timeline instead of clip start

**Variables:** `note.pitch`, `note.velocity`, `note.start`, `note.duration`, `note.probability`, `note.deviation`, `note.index` (time-ordered), `note.count` (MIDI), `next.pitch`, `next.velocity`, `next.start`, `next.duration` (next distinct-start note; skips chords; warns on last note), `audio.gain`, `audio.pitchShift` (audio), `clip.duration`, `clip.index` (order of ids), `clip.count`, `clip.position` (arrangement only), `clip.barDuration` (all clips)

```
timing = swing(0.05)             // swing (auto-quantizes). Use swing() alone unless asked for a specific grid
timing = quant(1/2t)             // snap to 8th-note grid (half a beat)
timing = quant(1/4t)             // snap to 16th-note grid (quarter beat)
timing += 0.05 * rand()          // humanize timing
velocity += 20 * cos(2t)         // cycle every 2 beats
velocity += 20 * cos(4:0t, sync) // continuous across clips
1|1-4|4.75: velocity = ramp(40, 127) // crescendo over 4 bars (16th grid)
C1-C2: velocity += 30            // accent bass notes
1|1-2|4: velocity = 100          // forte in bars 1-2
velocity = seq(100, 60, 80, 60)  // cycle accents per note
Gb1: pitch = seq(Gb1, Gb1, Gb1, Gb1, Ab1) // every 5th closed hat → open hat
gain = audio.gain - 6            // reduce audio clip by 6 dB
pitch = snap(note.pitch + 7) // transpose up fifth, snap to scale
pitch = step(note.pitch, sin(4t) * 7) // oscillate ±7 scale steps smoothly
pitch = wrap(note.pitch + 5, C3, C5) // transpose up 5, wrap within C3-C5
velocity *= 0.5                  // halve all velocities
C1-C2: duration /= 2             // halve duration of bass notes
duration = legato()              // extend each note to reach the next
duration = legato(0.1)           // legato with tolerance (after humanizing timing)
```

swing() auto-quantizes to the swing grid, so changing swing amount is always safe without a separate quant() step. Use `raw` to skip auto-quantize: `swing(0.05, raw)`

`+=` compounds on repeated calls; `=` is idempotent. `*=`/`/=` scale the current value (`timing *=` scales absolute note position). Use update-clip with only transforms to modify existing notes.
Transforms modify notes in place — previous transforms are already baked in. Don't re-apply earlier transforms.
MIDI params ignored for audio clips, vice versa.

#### Working with Ableton Live

**Views and Playback:**
- Session View: Jam, try ideas, build scenes
  - Use auto:"play-scene" when generating clips; warn user about clip restarts
- Arrangement View: Structure songs on a timeline
  - Session clips override Arrangement; use "play-arrangement" for arrangement playback

**Creating Music:**
- For drum tracks, read the track with `drum-map` include for correct pitches - don't assume General MIDI
- Use velocity dynamics (pp=40, p=60, mf=80, f=100, ff=120) for expression
- Keep harmonic rhythm in sync across tracks

**Layering:** To layer tracks on one instrument, duplicate with routeToSource=true. New track controls the same instrument.

**Locators:** Use ppal-update-live-set to create/rename/delete locators at bar|beat positions. Use locator names with ppal-playback to start or loop from named positions.

##### Device Paths

Slash-separated segments: `t`=track, `rt`=return, `mt`=master, `d`=device, `c`=chain, `rc`=return chain, `p`=drum pad

- `t0/d0` = first device on first track
- `rt0/d0` = first device on Return A
- `mt/d0` = first device on master track
- `t0/d0/c0/d0` = first device in rack's first chain
- `t0/d0/rc0/d0` = first device in rack's return chain
- `t0/d0/pC1/d0` = first device in Drum Rack's C1 pad

Chains are auto-created when referenced (e.g., `c0` on an empty rack creates a chain). Up to 16 chains.

##### Moving Clips

`arrangementStart` moves arrangement clips; `toSlot` (trackIndex/sceneIndex, e.g., "2/3") moves session clips. Moving clips changes their IDs - re-read to get new IDs.
`arrangementLength` sets arrangement playback region. `split` divides arrangement clips at bar|beat positions.

:::

::: details Basic Skills (small model mode)

#### MIDI Notation

Pitches: C0-G8 with # or b for sharps/flats (C#3, Bb2). C3 = middle C
Format: [v&lt;vel&gt;] [t&lt;dur&gt;] [p&lt;prob&gt;] pitch(es) bar|beat
- v: velocity 0-127 (default 100). t: duration in beats (default 1). p: probability 0-1 (default 1). Persist until changed
- Fraction beats: t/4 = quarter beat, t3/4, 1|2+1/3 for triplets

##### Melody (one note per beat across 2 bars)
```
C3 1|1 D3 1|2 E3 1|3 F#3 1|4
G3 2|1 A3 2|2 G#3 2|3 E3 2|4
```

##### Chords (set duration with t, t4 = 4 beats = full bar in 4/4)
```
t4
C3 E3 G3 1|1
D3 F3 A3 2|1
E3 G3 B3 3|1
F3 A3 C4 4|1
```

##### Drums (commas for multiple beats, {beat}x{count}[@{step}] for repeats)
```
C1 1|1,3 2|1,3 3|1,3 4|1,3  # kick
D1 1|2,4 2|2,4 3|2,4 4|2,4  # snare
t/4 Gb1 1|1.5x4@1 2|1.5x4@1 3|1.5x4@1 4|1.5x4@1  # hats (4 per bar, step 1 beat)
```

#### Rules
- Set clip lengths explicitly (e.g., 4:0 for 4 bars)
- Positions use | (bar|beat). Durations use : (bar:beat) or plain beats (4, 2.5)
- If the user references a track, get its trackIndex and id - never guess

:::
