ablx.directory

ReverseVerb

v1.0.0

One-click reverse reverb swell for Ableton Live 12 (Extensions SDK). Offline DSP, right-click an audio clip.

ImiteDevmanifest author: @mingus.dkNo ratings yet
README · rendered from GitHubView source

ReverseVerb

An Ableton Live 12 Extension that automates the reverse reverb swell into a single right-click on an audio clip.

The reverb tail, flipped in time, crescendos into the original transient — a swell that builds up to the hit. Great on vocals, snares, downbeats, and transitions. The whole signal path is owned in JavaScript and runs offline, so the result is deterministic and the source clip is never touched.

renderPreFxAudio(track, clipStart, clipEnd)   →  dry sample buffer
  → reverse the buffer
  → convolve with a synthesized reverb impulse response
  → reverse the result back
  → (optional) lay the original dry hit back at the transient
  → write a NEW audio clip on a NEW track

Requirements

  • Ableton Live 12 Suite Beta, v12.4.5+ with Developer Mode enabled (Preferences → Extensions). Extensions do not exist in Standard/Intro/Lite or the stable release.
  • Node.js ≥ 24.14.1.
  • The Ableton Extensions SDK + CLI tarballs, placed in ./sdk/. They are not included in this repo — Ableton's SDK license forbids redistributing the SDK itself. If you have access to the Extensions beta, download them and drop them in (see below).

Install & run (development)

You must supply the SDK yourself. From your Extensions beta download, place these into a ./sdk/ folder at the repo root:

sdk/ableton-extensions-sdk-1.0.0-beta.0.tgz
sdk/ableton-extensions-cli-1.0.0-beta.0.tgz

(These exact filenames are what package.json references via file:./sdk/....) Then:

npm install                       # resolves the SDK/CLI from ./sdk/*.tgz
cp .env.example .env              # then edit EXTENSION_HOST_PATH for your machine
npm start                         # builds and loads the extension into Live

npm start requires Live's beta build with Developer Mode on; without it the CLI can't connect. EXTENSION_HOST_PATH points at Live's Extension Host module on your machine.

Other scripts:

CommandWhat it does
npm testRuns the offline DSP unit tests (no Live needed).
npm run typechecktsc --noEmit.
npm run buildType-check + production bundle to dist/extension.js.
npm run packageBuilds and packages a distributable .ablx.

Usage

Right-click an audio clip in the Arrangement → you get two actions:

  • Reverse Reverb Swell — one click, processes with musical defaults.
  • Reverse Reverb Swell… — opens a settings dialog first.

The result is written to a new audio track placed so the original transient lands where the source clip began. The source clip is left untouched (undoable).

Whatever you set in Reverse Reverb Swell… is saved as your defaults (settings.json in the extension's storage directory) and is reused by both actions next time.

Parameters

ParamTypeDefaultNotes
decaySecondsfloat2.0Reverb length / swell length.
preDelayMsfloat0Front-pads the IR → a gap immediately before the hit.
dampingHzfloat6000One-pole low-pass tone on the tail.
wetMixfloat 0–11.0Wet swell level over the dry in the hit region (1.0 = pure swell on top of the dry).
appendDrybooltruetrue = swell + original dry hit (drop-in); false = swell only.
outputGainDbfloat0.0Final trim, applied after normalisation to −1 dBFS.
reverseBackbooltruetrue = reverse-reverb swell; false = plain forward reverb tail (for testing).

Reverb model

The impulse response is synthesized procedurally — no IR file, no external binaries. It is exponentially-decaying, gently low-passed, decorrelated stereo noise:

  • Length = decaySeconds at the clip's sample rate (+ preDelayMs of front padding).
  • Amplitude envelope exp(-t·k), k = 6.908 / decaySeconds (~−60 dB at the tail).
  • One-pole low-pass at dampingHz for tone.
  • Two decorrelated noise channels (deterministic, seeded) for stereo width; mono input is fed to both.

Convolution is FFT-based (a dependency-free radix-2 FFT), so multi-second decays don't stall the UI. The wet result is normalised to −1 dBFS before outputGainDb.

Output placement

The processed buffer is longer than the source — the swell precedes the hit. The dry transient lands swellLength (= IR length) into the buffer, so the new clip is placed swellLength earlier on the timeline and the hit realigns to the source clip's original start.

Caveats:

  • Always a new track. The processed buffer spans the source's own timeline region, so reusing the source track would overwrite it. A dedicated new audio track is the simplest provably non-destructive choice. (Smart same-track placement is future work — see below.)
  • Clip near bar 1. If the source starts less than swellLength from the timeline start, the new clip is clamped to bar 1 and the transient may not perfectly realign (a warning is logged).

Verified SDK API notes

These were confirmed against the bundled types (sdk/.../dist/index.d.mts), docs, and examples before writing the core logic:

  1. Raw rendercontext.resources.renderPreFxAudio(track, startBeat, endBeat) returns a path to a WAV in the extension's temp dir. It renders a track over a beat range (not a clip object), pre-effects. We resolve the clip's owning AudioTrack (walking parent), render over the clip's startTime/endTime, then read + decode the WAV with audio-decode.
  2. Write back — write our processed WAV into environment.tempDirectory (extensions are sandboxed to the temp/storage dirs), then resources.importIntoProject(path)track.createAudioClip({ filePath, … }). Inserting our own generated buffer as a new clip is fully supported.
  3. Undo — clip creation is wrapped in context.withinTransaction(…) (a synchronous callback returning the mutation's promise).
  4. Parameters / UI — there is no rich parameter system in the beta. UI is limited to showModalDialog (a webview that posts a result string) and withinProgressDialog. We expose all parameters through a small HTML form loaded from a self-contained data: URL, plus a one-click defaults action.
  5. Context-menu scopingui.registerContextMenuAction("AudioClip", …) scopes the action to audio clips and passes the clip's Handle to the command, resolved via getObjectFromHandle(handle, AudioClip).
  6. Native reverse — not present; reversing is done in JS.

Project layout

ReverseVerb/
├── manifest.json          Extension manifest (name, entry, API version)
├── build.ts               esbuild bundler (→ dist/extension.js)
├── src/
│   ├── extension.ts       Host glue: context menu, render → process → new clip
│   ├── dsp.ts             Pure DSP: synthIR, reverse, normalize, gain, processor
│   ├── fft.ts             Dependency-free radix-2 FFT + FFT convolution
│   ├── wav.ts             32-bit float WAV encoder
│   └── params.ts          Defaults, sanitisation, settings-modal HTML
├── test/
│   └── dsp.test.ts        Offline DSP unit tests (Node test runner, no Live)
└── sdk/                   SDK/CLI tarballs — you supply these (gitignored)

Scope

v1: a single selected arrangement audio clip.

Out of scope (future work): multi-clip batch, smart same-track placement when there's room, IR-file loading, gated/ducked variants, automation lanes, MIDI, and Session-view clips.

License

This project's own source code is released under the MIT License.

The Ableton Extensions SDK is not covered by that license and is not included here. It is Ableton's, under their own SDK license, which forbids redistributing the SDK kit. You may freely share a packaged .ablx you build (the SDK code compiled into it is permitted "inside your application"), but not the sdk/ tarballs, docs, or examples.

Rate this extension

One rating per account; you can change it any time.

No ratings yet — be the first

Comments

Sign in to join the discussion.Sign in

No comments yet.