Differentiable, cross-vendor, framework-agnostic audio DSP — a functional graph API, a modern SoX-substitute CLI, and a hard-real-time engine. Written in Rust, bound to anything.
Fluxion owns the DSP-specific pieces that determine correctness and deployability —
closed-form filter design, forward kernels, analytic adjoints for exact gradients, stability
certificates — and rents the general-purpose infrastructure that ML systems already provide:
the autodiff tape, portable GPU code generation, array interchange. One graph algebra lowers to
two engines that never mix their rules: a batched differentiable training engine (CPU SIMD /
CUDA) and an allocation-free, lock-free real-time engine. A certified freeze boundary
connects them: a trained graph becomes a versioned .fxg artifact that an edge device
re-certifies before playback and can hot-swap live with a click-free crossfade.
This repository is the software companion to the paper Fluxion: A Differentiable DSP Runtime from Training GPU to Real-Time Edge (IS² 2026) — see Citation and Reproducing the paper.
Status: pre-1.0 (0.x). The core is implemented and tested: the graph algebra, the DSP op set with hand-derived analytic gradients, Burn-based whole-graph autodiff, CPU SIMD + CUDA batch kernels, the allocation-free realtime engine, audio IO, the CLI, and the Python package. The public API and the
.fxgon-disk format may still change before 1.0.Roadmap: the host-engine push — WebAssembly first, then streaming resampling, the mastering set, sidechain routing, analysis taps, the timeline helpers, and one simple API across CLI, Python, C and the browser — is planned task-by-task, with dependencies and verifiable milestones, in ROADMAP.md.
One codebase, five ways in:
- Rust library —
fluxionon crates.io: compose effects with|(series) and+(parallel), run them batched on CPU/GPU, differentiate them, or freeze them for realtime. - CLI —
fluxion, a SoX substitute with named effects and long flags (not SoX's interface, most of SoX's jobs). - Python —
fluxionon PyPI: a torchfx-style API (Wave, effect classes,|and+), zero-copy DLPack interop, torch/JAX autograd adapters, batched data augmentation. - C ABI — a small panic-safe surface (
fluxion.h) for C/C++/Swift consumers. - Browser —
npm install fluxion: the same engine as WebAssembly, offline render today, AudioWorklet playback next. Its output is checked against the native library in CI.
All of them build the same graph and share one text form for it —
"highpass(80, 4) | gain(-3dB)" means the same thing everywhere. See
docs/interfaces.md for the contract between them and
docs/ops.md for every op's name on every interface.
# Rust library
cargo add fluxion # batch/CPU core (pure Rust, builds offline)
cargo add fluxion -F autodiff # + whole-graph differentiation through Burn
cargo add fluxion -F realtime # + the realtime engine re-exports
# CLI
cargo install fluxion-cli # file processing, no audio-device deps
cargo install fluxion-cli --features realtime # + play/record via CPAL
# Python (wheels: Linux x86_64/aarch64, macOS Intel/AS, Windows; numpy is the only hard dep)
pip install fluxion # extras: [torch] [jax] [interop] (safetensors) [dataset] (parquet)Dependencies are deliberately thin: the default build is pure Rust and compiles offline; heavy optional stacks (Burn/CubeCL for autodiff/GPU, CPAL for audio devices, PyO3 for Python) only enter behind the feature flags that need them.
use fluxion::prelude::*;
// `|` = series, `+` = parallel (outputs summed) — the same algebra everywhere.
let chain = (lowpass(800.0, 2) + highpass(4000.0, 2)) | compand(0.01, 0.1, -20.0, 4.0, 6.0, 0.0) | gain(0.5);
// The same chain, written in the text syntax the CLI, Python, C and JS all share.
let same: Graph = "(lowpass(800, 2) + highpass(4000, 2)) | compand(0.01, 0.1, -20, 4, 6, 0) | gain(0.5)".parse()?;
assert_eq!(same, chain);
let wet = process(&chain, &signal); // batch: any channels, allocates freelyThe same graph lowers to three executors, never mixing their rules:
// Differentiable (feature `autodiff`): loss.backward() flows through the whole chain —
// every op owns its analytic VJP; Burn provides the tape. Train coefficients or design
// parameters ("learn a cutoff") with stability guaranteed by construction.
let out = fluxion::diff_process::<B>(&chain, x, fs);
// Realtime (feature `realtime`): freeze designed coefficients, then run alloc-free,
// lock-free, bounded-time blocks in the audio callback — no autograd, no GPU, no locks.
let mut rt = fluxion::to_rt_graph(&chain, fs).expect("realtime-lowerable");
rt.prepare(128);
rt.process(&mut block);Geometry operations that change length, channel count, or sample rate (trim, pad, resample,
remix, …) are deliberately not graph ops — they live in fluxion::transform and run between
graph passes.
Same philosophy, modern interface: named effects, long --flags, explicit units (Hz, seconds,
dB), SI suffixes (--cutoff 1k), and a self-describing catalog (fluxion effects).
# Filter a file: adjacent effects fuse into one pass.
fluxion in.wav lowpass --cutoff 1k gain --db -6 out.wav
# The bread-and-butter SoX jobs: trim, resample, bit-depth conversion (TPDF-dithered).
fluxion --bits 16 in.flac trim --start 0.25 --len 30 rate --fs 44100 out.wav
# Multiple inputs: concatenate by default, sum with --mix.
fluxion --mix vocals.wav backing.wav mixed.wav
# Generate, analyze, inspect.
fluxion synth --wave sine --freq 440 --secs 1 fade --fadein 0.1 tone.wav
fluxion stat in.wav # min/max, peak & RMS dBFS, DC offset, crest factor
fluxion info in.mp3 # metadata for WAV/FLAC/MP3/OGG/… (alias: soxi)
# Unix filter, null sink, dataset batch.
fluxion - reverse - < in.wav > out.wav
fluxion batch out/ 'data/*.wav' highpass --cutoff 80
# Freeze a chain to a portable artifact (stability-certified), play it live.
fluxion compile lowpass --cutoff 800 echo --time 0.3 chain.fxg
fluxion play in.wav chain.fxg # --features realtime
fluxion record --secs 5 take.wav # --features realtime
# Import a DDSP checkpoint trained elsewhere (FLAMO / torchfx), certified on the way in.
fluxion import ckpt.safetensors model.fxgEffects (graph ops): gain, lowpass/highpass (Butterworth, any order),
cheby1_lowpass/cheby1_highpass/cheby2_lowpass/cheby2_highpass, RBJ
peaking/lowshelf/highshelf/
notch/bandpass/allpass, raw biquad, fir --taps …, normalize, delay, echo,
reverb, fade, tremolo, overdrive, compand, reverse, chorus, flanger, phaser.
Geometry stages: trim, pad, rate, speed, repeat, silence, channels, remix.
Run fluxion effects [name] for every parameter, unit, and default.
Not ported from SoX (yet or ever): tempo/pitch (time-stretch), spectrogram
(imaging dependency), noise reduction, and legacy niches (oops, riaa, earwax).
import fluxion as fx
from fluxion import Compose, RandomChain
# Wave carries fs, so it never appears in the chain. `|` is series, `+` is parallel.
wave = fx.Wave.from_file("in.wav")
(wave | fx.filter.Highpass(80, order=4) | fx.effect.Gain(fx.db(-3))).save("out.wav")
chain = fx.filter.Lowpass(8000) | fx.effect.Gain(0.5) # same algebra as Rust and the CLI
chain = fx.chain("lowpass(8000) | gain(0.5)") # ...or the shared text form
y = chain(x, fs=48_000) # (T,) or (C, T); numpy/torch/jax via DLPack
ys = chain.process_batch(batch, fs=48_000) # (B, T) batched
# Data augmentation: stochastic chains, seeded.
aug = Compose([RandomChain(fx.filter.Lowpass, cutoff=(2_000, 16_000), p=0.8)])
x_aug = aug(x, fs=48_000)
# Dataset IO (extra: fluxion[dataset]) — Parquet, same schema as the Rust side, streamed.
from fluxion.dataset import iter_parquet, write_parquet
write_parquet("out.parquet", # augment a whole dataset, lazily
((aug(x, fs), fs) for x, fs in iter_parquet("in.parquet")))
# Training: coefficients as nn.Parameter, analytic backward under torch autograd.
from fluxion.torch import SosModule
mod = SosModule.from_chain(chain, fs=48_000)
# Import a FLAMO-trained SISO biquad cascade (extra: fluxion[interop]).
coeffs = fx.interop.load_flamo_sos("checkpoint.safetensors")| Crate | Role |
|---|---|
fluxion (crates/fluxion-facade) |
Facade + prelude; the crate users depend on (features: autodiff, realtime) |
fluxion-core |
Graph algebra + IR (| series, + parallel, ~ feedback), typed op catalog, versioned .fxg |
fluxion-ops |
DSP kernels + analytic VJPs, coefficient design, geometry transforms (SciPy golden-vector oracle tests) |
fluxion-backend |
CPU executor (SIMD batch path), graph lowering, stability certification, CUDA kernels (feature cuda) |
fluxion-autodiff |
Burn Autodiff integration: whole-graph diff_process, trainable coeffs/design params (feature-gated) |
fluxion-rt |
Realtime engine: lock-free SPSC ring, alloc-free executor (alloc-asserted tests), CPAL backend (feature cpal) |
fluxion-io |
WAV read/write (16/24/32-bit, TPDF dither), Symphonia decode/probe (FLAC/MP3/OGG/AAC/…), bounded-memory streaming, Arrow/Parquet dataset IO (feature parquet), FLAMO/torchfx DDSP checkpoint import (feature checkpoint) |
fluxion-cli |
The fluxion binary (feature realtime for play/record) |
fluxion-py |
PyO3/maturin package fluxion (abi3, numpy-only hard dep; extras: torch/jax/interop/dataset) |
fluxion-ffi |
C ABI (include/fluxion.h, cbindgen), panic-safe; publish = false |
fluxion-wasm |
wasm-bindgen browser bindings: chain from text + offline render (CPU). AudioWorklet and WebGPU deferred; publish = false |
GPU status: CUDA forward + backward kernels are implemented and validated on NVIDIA hardware;
Apple Metal / AMD ROCm validation via CubeCL is pending, so GPU stays behind the cuda feature.
The measurement harnesses used in the paper's evaluation ship as ordinary cargo examples
(release builds; each prints JSON lines or a summary):
| Experiment | Command |
|---|---|
| CPU batch throughput | cargo run --release -p fluxion-backend --example paper_bench |
| Training-step throughput (analytic adjoint) | cargo run --release -p fluxion-autodiff --example train_step_bench --features burn |
| EQ inversion: train → certify → freeze → play | cargo run --release -p fluxion-autodiff --example eq_inversion --features burn |
| Real-time callback latency CCDF | cargo run --release -p fluxion-rt --example latency_ccdf |
| 1024-tap FIR per-block latency | cargo run --release -p fluxion-rt --example fir_latency |
| Certified live hot-swap loop | cargo run --release -p fluxion-backend --example hotswap_demo |
| Multichannel per-channel-chain case study | cargo run --release -p fluxion-backend --example soundlamp_demo |
Two didactic examples show the differentiable path in miniature: learn_cutoff
(-p fluxion-autodiff --features burn) trains a low-pass cutoff by gradient descent, and
fit_filter (-p fluxion-ops) fits biquad coefficients with the analytic gradient alone.
cargo build && cargo test # whole workspace (offline, pure Rust)
cargo clippy --all-targets # lints are CI-gated (-D warnings), incl. missing_docs
cargo bench -p fluxion-ops # Criterion benchmarks
cargo run -p fluxion-cli -- effects
cd crates/fluxion-py && maturin develop && pytest tests/ # Python packageSee CONTRIBUTING.md for the conventions CI enforces.
MIT. No GPL code from the reference projects (SoX, AudioNoise) — ideas and math only.