Build audio plugins in Rust.
CLAP, VST3, LV2, AU v2, AU v3, AAX, and standalone — from a single Rust crate. Install and load your plugin in a DAW in five minutes.
# Install the CLI (one-time)
cargo install cargo-truce
# Scaffold a new plugin
cargo truce new my-plugin
cd my-plugin
# Run the plugin standalone — no DAW needed
cargo truce run
# Build and install
cargo truce install --clap
cargo truce install --vst3
# Open your DAW, scan for plugins, load "MyPlugin"Overview
Capabilities
One Rust codebase compiles to seven plugin formats. Hot reload, declarative params, cross-platform installers, automated validation.
Every format, every platform
CLAP and VST3 by default; VST2, LV2, AU v2, AU v3, and AAX opt-in per crate. macOS, Windows, Linux, and iOS from one Rust codebase - the CLI handles signing, notarization, installers, and validation.
Real-time safe
No locks or allocations in process(). Write 32- or 64-bit DSP, with the host's native f64 wire taken directly where it exists. Opening the GUI and saving state stay lock-free, so neither stalls the audio thread.
Off-thread work
Offload FFTs, graph rebuilds, and file decodes off the audio thread. A managed pool runs discrete tasks; an AudioTap plus a dedicated worker stream samples out for spectrum analysis. Results flow back lock-free.
MIDI 2.0 & multi-port
Opt-in MIDI 2.0 / UMP and multiple MIDI in/out ports, with per-note expression (MPE) mapped across CLAP, VST3, and AU v3. MIDI 1.0 plugins are unchanged.
Params and your GUI
#[derive(Params)] with ranges, units, and smoothing; atomic, lock-free access from any thread. Render them with built-in widgets, egui, iced, Slint, Vizia, or a raw window handle.
Hot reload
Edit DSP or layout, rebuild, hear changes without restarting your DAW.
Compatibility
Format support
CLAP and VST3 ship as defaults. VST2, LV2, AU, and AAX are opt-in per plugin via Cargo features.
| Format | macOS | Windows | Linux | iOS |
|---|---|---|---|---|
| CLAP | Yes | Yes | Yes | — |
| VST3 | Yes | Yes | Yes | — |
| VST2 | Yes | Yes | Yes | — |
| LV2 | Yes | Yes | Yes | — |
| AU v2 | Yes | — | — | — |
| AU v3 | Yes | — | — | Yes |
| AAX | Yes | Yes | — | — |
Example
A complete plugin
Smoothed parameter, built-in knob, CLAP + VST3 + standalone. The truce::plugin! macro generates every format export, GUI, and state serialization.
use truce::prelude::*;
use truce_gui::IntoLayoutEditor;
use truce_gui_types::layout::{knob, widgets, GridLayout};
#[derive(Params)]
pub struct GainParams {
#[param(name = "Gain", range = "linear(-60, 6)",
unit = "dB", smooth = "exp(5)")]
pub gain: FloatParam,
}
use GainParamsParamId as P;
// A stateless descriptor. Parameters live in GainParams; this gain
// carries no per-instance DSP state, so it implements PurePluginLogic.
// (A plugin with DSP state - filter memory, oscillator phase - puts it
// in a #[derive(Default)] struct GainDspState { .. } and implements
// PluginLogic with type DspState = GainDspState instead.)
pub struct Gain;
impl PurePluginLogic for Gain {
type Params = GainParams;
fn process(params: &GainParams, buffer: &mut AudioBuffer,
_events: &EventList, _ctx: &mut ProcessContext) -> ProcessStatus {
for i in 0..buffer.num_samples() {
let gain = db_to_linear(params.gain.read());
for ch in 0..buffer.channels() {
let (inp, out) = buffer.io(ch);
out[i] = inp[i] * gain;
}
}
ProcessStatus::Normal
}
fn editor(params: Arc<GainParams>) -> Box<dyn Editor> {
GridLayout::build(vec![widgets(vec![knob(P::Gain, "Gain")])])
.into_editor(¶ms)
}
}
truce::plugin! { logic: Gain, params: GainParams }