A guide to Julius O. Smith III's online textbook Audio Signal Processing in Faust — the functional DSP language invented at Grame by Orlarey, Fober & Letz, and the block-diagram algebra that makes it expressive enough to capture a Karplus-Strong string, an 8-channel feedback delay network, or a compressor in a handful of lines.
Julius O. Smith IIIYann OrlareyDominique FoberStéphane LetzMax Mathews
Faust (Functional AUdio STream) is a domain-specific language for real-time
digital signal processing. A Faust program is a single algebraic expression that
describes a block diagram; the compiler turns that expression into efficient
C++, Rust, LLVM IR, JavaScript or WebAssembly. One thirty-line file becomes a
VST plug-in, an iOS app, an ESP32 firmware image, or a web-audio node.
The language was designed in 2002 at Grame
(Lyon) by Yann Orlarey, Dominique Fober and Stéphane Letz, with an explicit
goal: replace the error-prone loops and buffer pointers of C/C++ DSP with a
purely functional, sample-accurate, algebraic notation that you could read the
way an electronics engineer reads a block diagram. Julius O. Smith III — long
associated with the CCRMA lab at Stanford and author of the four-volume
Mathematics of the DFT / Introduction to Digital Filters / Physical Audio
Signal Processing / Spectral Audio Signal Processing tetralogy — adopted
Faust as the lingua franca of his fifth online book,
Audio Signal
Processing in Faust, which this guide accompanies.
What Faust buys you
Property
Consequence
Purely functional
no mutable state outside ~; referentially transparent; trivially parallel
Typed signals
every expression has a signature (n → m); compiler catches channel mismatches at compile time
Block-diagram semantics
five composition operators (: , <: :> ~) match exactly what you draw on paper
Sample-accurate
no block-size assumption in the source; compiler vectorises as it pleases
the compiler can print the normalised block diagram and the symbolic signal expression
A first example — a one-pole lowpass in three tokens
// A one-pole smoother with pole at a = 0.99.process=+~*(0.99) :_;
Read left to right: the input feeds an adder; the adder's output feeds a
multiplier by 0.99; the multiplier's output feeds back into
the adder via ~, which the compiler interprets as a single-sample
delay in the feedback path. The trailing : _ routes the result
to the output. The difference equation is
$$y[n] \;=\; x[n] + 0.99\,y[n-1]$$
and the transfer function is $H(z) = 1/(1-0.99\,z^{-1})$. Three tokens of Faust,
an hour of C, and an entire afternoon of MATLAB.
This guide
We will rebuild Faust's block-diagram algebra from scratch, translate its type
system into plain English, then work through the progression laid out in JOS's
book: oscillators, delay lines, recursive filters, classical filter topologies
(biquad, allpass, comb, Schroeder, FDN), physical models (Karplus-Strong,
waveguides), and three worked case studies (channel strip, reverb, nonlinear
shelf). A final chapter covers the toolchain — Faust IDE, the faust2…
script family, the standard libraries — and Faust's continuing role in
research on differentiable DSP and neural audio.
Every Faust listing on this page is copy-pasteable into the
Faust IDE. Every JavaScript
demo computes the same algorithm in-browser via Web Audio so you can hear the
result without compiling anything.
2A History of Audio Programming Languages
Faust did not arrive in a vacuum. It is the youngest surviving member of a
sixty-year lineage that began with Max Mathews pointing the first digital
audio loudspeaker at a room full of Bell Labs engineers in 1957 and playing
them a computer-generated melody. Every language since has answered the same
two questions in a different order: how do you describe a signal flow,
and how fast do you want it to run?
1957 — Music I
Max Mathews at Bell Labs writes Music I on an IBM 704 — a single triangle oscillator, offline, non-real-time, the first digital synthesiser. By Music III (1960) the language introduces unit generators, the enduring abstraction. Music V (1969) is portable Fortran.
1986 — CSound
Barry Vercoe at MIT Media Lab releases CSound, a direct C descendant of Music-N. Separates an orchestra file of unit-generator instruments from a score file of notes. Still in active use forty years later.
1988 — Max
Miller Puckette at IRCAM writes Max (named after Mathews) — graphical patching of MIDI and control-rate objects. David Zicarelli ports it to the Macintosh; MSP (1997) adds audio-rate signal objects and it becomes Max/MSP. Commercial, closed-source.
1996 — Pure Data
Puckette's open-source response to Max/MSP. Same dataflow patching, free, cross-platform. Still the vehicle of choice for intermedia artists and installation work.
1996 — SuperCollider
James McCartney writes SuperCollider — a Smalltalk-inspired object-oriented language with a scsynth audio server. Text-based, just-in-time compiled, live-codable. Went open source in 2002 and is the backbone of the live-coding scene (TidalCycles runs on it).
2002 — Faust
Yann Orlarey, Dominique Fober and Stéphane Letz at Grame introduce Faust at the International Computer Music Conference. Functional, statically typed, block-diagram-algebraic, compiled ahead-of-time to C++. Open source from day one.
2003 — ChucK
Ge Wang and Perry Cook at Princeton release ChucK — a strongly-timed concurrent language where time is a first-class type. Aimed at live coding and teaching.
2010s — The WASM era
Grame releases faust2webaudio and the browser-based Faust IDE; SuperCollider gains scsynth.js; JUCE embeds a Faust-JIT compiler. Real-time audio DSP runs in a browser tab.
2020s — Differentiable
The functional-pure, single-expression nature of Faust makes it an unusually clean target for automatic differentiation. Research projects (DDSP, neural Faust, faust-torch) back-propagate through Faust programs to learn DSP parameters from audio data.
Where Faust fits
Language
Paradigm
Execution
Graphical
CSound
unit-generator / imperative
interpreted C engine
no
Max/MSP, Pd
dataflow
interpreted
yes
SuperCollider
object-oriented / client-server
JIT + C engine
no
ChucK
strongly-timed concurrent
interpreted VM
no
Faust
pure functional, block-diagram algebra
ahead-of-time compiled to C++/Rust/WASM/LLVM
no (but prints diagrams)
Max Mathews
1926 – 2011
American electrical engineer at Bell Labs; father of computer music. Wrote Music I-V between 1957 and 1969. Late in life at CCRMA championed the radio-baton controller.
Miller S. Puckette
b. 1959
Wrote Max at IRCAM (1988) and Pure Data (1996). Professor at UCSD. Long collaborator with Philippe Manoury on real-time score following.
Roger Dannenberg
b. 1955
Carnegie Mellon; author of Nyquist (Lisp-based music language) and the CMU MIDI Toolkit. Co-developed score-following in the 1980s independently of Vercoe.
Perry R. Cook
b. 1955
Princeton; wrote the Synthesis ToolKit (STK) and co-invented ChucK with Ge Wang. Long collaborator with JOS on physical modelling.
Yann Orlarey
b. 1959
Scientific director of Grame (Lyon); principal designer of Faust. Background in functional programming & lambda calculus; Faust is a direct descendant of that tradition.
Dominique Fober
Grame research engineer
Co-designer of Faust; works on music representation, INScore (animated scores) and the Guido score notation system.
Stéphane Letz
Grame research engineer
Co-designer of Faust; wrote JACK for OS X and much of Faust's JIT-LLVM and WASM backend infrastructure.
Julius O. Smith III
b. 1953
CCRMA (Stanford). Doctoral work on digital waveguides; authored the four-volume online textbook series and the Faust companion Audio Signal Processing in Faust. His courses Music 220 and 320C are the canonical academic introduction to Faust.
3Faust Block-Diagram Algebra
The whole of Faust is five composition operators acting on primitive boxes.
Every program — a oscillator, a reverb, a compressor — is a single
algebraic expression built from these. Once you see them as block-diagram
constructors, Faust syntax vanishes and you are just reading wiring diagrams.
The five operators
Operator
Name
Block diagram meaning
Type rule
A : B
sequential
outputs of A wired to inputs of B
(a→b) : (b→c) ⇒ (a→c)
A , B
parallel
A and B placed side-by-side, disjoint buses
(a→b) , (c→d) ⇒ (a+c → b+d)
A <: B
split
each output of A fan-out-broadcast to inputs of B
(a→b) <: (kb→c) ⇒ (a→c), k ∈ ℕ
A :> B
merge
outputs of A summed k-at-a-time to inputs of B
(a→kb) :> (b→c) ⇒ (a→c)
A ~ B
recursive
outputs of A fed back through B, via a 1-sample delay
(a+c→b) ~ (b→c) ⇒ (a→b)
Operator precedence (tightest first): ~ , , , : , <: , :>.
Parentheses disambiguate. The recursion operator ~ is magic: Faust
inserts a one-sample delay in the feedback path so the expression is
well-defined (otherwise the compiler would need to predict the future).
Primitive boxes you will meet everywhere
Box
Signature
Meaning
_
1 → 1
identity (through-wire)
!
1 → 0
cut (drops the signal on the floor)
+, -, *, /
2 → 1
sample-wise arithmetic
0.5
0 → 1
constant 0.5
@(d)
1 → 1
delay of d samples
mem
1 → 1
1-sample delay (= @(1))
+(0.5)
1 → 1
partial application: add 0.5
Partial application is a functional-programming trick: + takes two signals,
but +(0.5) has already been given one of them (the constant 0.5) and
therefore takes only one signal. This is the reason process = _ : +(0.5)
compiles to "add 0.5 to the input".
Worked example — the five operators on paper
// A: sequential ─ two gains in seriesA=*(0.7) :*(1.2); // (1 → 1) : (1 → 1) ⇒ 1 → 1// B: parallel ─ stereo pair of independent gainsB=*(0.7) ,*(1.2); // (1→1) , (1→1) ⇒ 2 → 2// C: split ─ one input fed to both channels of a stereo pairC=_<:*(0.7) ,*(1.2); // (1→1) <: (2→2) ⇒ 1 → 2 (mono → stereo)// D: merge ─ stereo summed to mono then gainD=_,_:>*(0.5); // (2→2) :> (1→1) ⇒ 2 → 1 (stereo → mono)// E: recursive ─ the one-pole smoother we have already metE=+~*(0.99); // (1+1→1) ~ (1→1) ⇒ 1 → 1
Block-diagram construction kit
Pick two primitive boxes and an operator. The panel below prints the resulting
Faust expression, draws the block diagram, and reports the signal-type signature.
Top: block diagram drawn from the two boxes and operator you chose. Bottom: Faust expression and the inferred (ins → outs) type.
4The Type System
Faust's types are deliberately small. There are scalar types for individual
samples, and there is one signal type: an ordered tuple of scalar streams with
a known cardinality and a known sample rate.
Scalar types
Name
What it is
Example literal
int
32-bit signed integer
42, -3
float
single-precision IEEE 754 by default (compile flags select double or quad)
0.5, 3.14e-2
bool
represented as 0 / 1 float, returned by comparison operators
x < 0.5
Signal types — the (n → m) signature
Every Faust expression has a type of the form $(n \rightarrow m)$, read
n inputs, m outputs. The compiler infers this by structural induction
over the five operators and the primitive boxes. Type errors are reported
as mismatches in cardinality — for example _ : (_,_) fails
because the left side produces one signal but the right side consumes two.
Sample rates
Most Faust boxes run at the full audio sample rate. Some — notably
UI controls (hslider, vslider, button,
checkbox) — run at control rate: the compiler is
free to evaluate them once per block rather than once per sample. The
si.smoo and si.polySmooth helpers in
signals.lib exist precisely to interpolate a control-rate slider
up to audio rate without zipper noise.
A complete minimal program
import("stdfaust.lib");
// Two sliders at control rate...gain=hslider("gain", 0.5, 0, 1, 0.01) :si.smoo;
bias=hslider("bias", 0.0, -1, 1, 0.01) :si.smoo;
// ...applied to a single mono input.process=_:+(bias) :*(gain) :*(0.8);
The type of process is $(1 \rightarrow 1)$: one input channel, one
output. If the input were stereo we would write
process = (_:+ (bias):*(gain)) , (_:+ (bias):*(gain)) or, more
idiomatically, process = par(i, 2, +(bias) : *(gain));.
The par(i, N, expr) iterator, together with seq, sum
and prod, lets you write a chain of any length in constant source
size. A 32-channel limiter is the same three lines as a 2-channel one.
5Basic Oscillators
The simplest useful Faust programs are oscillators. They also make a nice
tour of the language: a phasor exposes recursion; a sawtooth exposes
aliasing; a wavetable sine exposes table lookup; a polyBLEP exposes the
library ecosystem.
5.1 The ramp phasor
A phasor is a 0 Hz-to-Nyquist sawtooth that ramps from 0 to 1 at a
controlled frequency. In Faust:
import("stdfaust.lib");
// ramp = fractional counter that wraps at 1.0phasor(f) =f/ma.SR: (+:ma.frac) ~_;
process=phasor(220); // 220 Hz ramp 0…1
The ~ _ part is Faust shorthand for "feed the output back into the
adder through a one-sample delay", exactly as in Ch 3. The ma.frac
box (from maths.lib) keeps the counter in [0, 1). Divide a sawtooth
by 0.5 and subtract 1 and you have a symmetric bipolar ramp.
5.2 A sine by table lookup
Faust's standard library defines os.osc(f), an anti-aliased
wavetable sine. Internally it is a phasor driving a 1024-sample cosine
table with linear interpolation:
// Idealised – the real implementation lives in oscillators.lib.osc(f) =phasor(f) *N:rdtable(N, sineTable, _)
with {
N=1024;
sineTable=ba.time:*(2.0*ma.PI/N) :sin;
};
5.3 Aliasing and the polyBLEP fix
A raw sawtooth has a discontinuity every period. Its Fourier series
$\sum_{n} (-1)^{n+1}/n$ does not decay fast enough, so at sample rate 44.1 kHz
a 5 kHz sawtooth contains energy above Nyquist that folds back as audible
aliasing. The standard fix is the polyBLEP (polynomial band-limited
step) correction of Välimäki & Huovilainen: subtract a pre-computed
residual around each discontinuity. Faust's
os.sawtooth(f) and os.square(f) implement this out of the
box.
Interactive: hear the oscillators
Left: time-domain waveform for one period. Right: magnitude spectrum via a 1024-point FFT. Try saw-naive vs saw-polyBLEP at 2 kHz to see aliases fold back.
6Delay Lines and Filters
Every linear time-invariant system can be built from delays, adders and
multipliers. Faust gives you @(d) for integer delays,
de.delay(maxN, n, x) for variable-length delays, and
de.fdelay(maxN, n, x) for fractional delays. The filter library
filters.lib sits on top.
The one-pole lowpass, revisited
import("stdfaust.lib");
// y[n] = (1-a)·x[n] + a·y[n-1]lp1(a) =*(1.0-a) :+~*(a);
// Cutoff f_c at -3 dB ⇒ a = exp(-2π f_c / SR)process=lp1(exp(-2.0*ma.PI*1000.0/ma.SR));
The biquad — direct-form II transposed
A biquad implements any second-order transfer function:
Left: pole-zero plot on the unit circle. Right: magnitude response in dB, computed by evaluating H(e^{jω}) at 512 frequencies. Audio test: pink noise through the filter.
7Recursion — the Core of Feedback Filters
The ~ operator is the single feature that separates Faust from a
glorified pipeline language. Understanding it precisely is the key to reading
any non-trivial Faust program.
Semantics in one paragraph
The expression A ~ B requires A to have type
$(n_a + k \rightarrow n_b)$ and B to have type $(n_b \rightarrow k)$.
The compiler wires the $n_b$ outputs of A into B; B's $k$ outputs feed back
into A's last $k$ inputs, but through a one-sample delay. The result has
type $(n_a \rightarrow n_b)$. The mandatory delay is what makes the circular
equation well-defined.
Worked derivation: + ~ *(a)
Take A = + (the 2→1 adder) and B = *(a) (a 1→1
gain). Then A ~ B has type $(1 \rightarrow 1)$. The difference
equation is
One pole at $z = a$. For $|a| < 1$ the filter is stable; at $a \to 1$ it
becomes an (ideal) integrator; for $|a| > 1$ it explodes. The impulse response
is $h[n] = a^n \cdot u[n]$ (a geometric decay), and the magnitude response is
Interactive: vary a, see the pole and the impulse response
Left: z-plane with unit circle and pole at z=a. Middle: impulse response $h[n]=a^n$. Right: magnitude response in dB.
Why Faust inserts the delay
Consider y = y. As a mathematical equation this is satisfied by any
$y$; as a causal real-time program it specifies nothing. Faust avoids the
ambiguity by defining A ~ B to mean A's output at sample n is
computed from B's output at sample n−1. Equivalently: the compiler
inserts a mem in the feedback path. The cost is one sample of group
delay inside every feedback loop — which is exactly right for a digital
filter.
Multi-dimensional feedback
~ generalises beyond scalar feedback. A state-space block that
consumes $n$ inputs and produces $n$ outputs can be closed on itself by
A ~ si.bus(n), giving a self-recurrent MIMO system with $n$
one-sample feedback wires. This is how an 8x8 feedback delay network
(Ch 8) is written.
8Composition Patterns for Standard Filters
Once you are fluent in the five operators and ~ you can build
classical filter structures from first principles — the library versions are
then obvious re-uses.
8.1 Resonant biquad from scratch
The canonical RBJ resonant lowpass (Robert Bristow-Johnson 1994) is a
biquad with coefficients
// 1st-order allpassap1(a) =_<: (*(-a),_) :+~*(a);
// Feedback combfbcomb(N, g) =+~ (@(N) :*(g));
// Schroeder allpass (Manfred Schroeder 1962, JASA 34)schroederAP(N, g) = (+<:@(N),*(g)) ~*(-g) : (+:_);
// A chain of four Schroeder allpasses — the core of many vintage reverbsprocess=seq(i, 4, schroederAP(225+347*i, 0.7));
8.3 An 8x8 feedback delay network in a dozen lines
A feedback delay network (Jot & Chaigne 1991; Stautner & Puckette 1982)
is a bank of $N$ delay lines whose outputs are mixed by an orthogonal
$N\times N$ matrix and fed back to the inputs. Choosing a unitary mixing
matrix makes the network lossless — energy is preserved until per-loop
attenuation is added. The simplest stable choice is the Householder
matrix $U = I - \frac{2}{N}\mathbf{1}\mathbf{1}^T$, which is orthogonal with
a single multiply-and-sum per output:
import("stdfaust.lib");
// Prime-number delay lengths avoid flutter echoesDL= (601,691,773,853,907,1049,1109,1151);
N=8;
g=0.93; // per-delay gain; controls T60// 8 parallel delays with per-loop attenuationdelays=par(i, N, @(ba.take(i+1, DL)) :*(g));
// Householder mixing matrix I - (2/N) 1·1^Tmix= (si.bus(N) <:par(i, N, _), (si.bus(N) :>*(2.0/N)))
:ro.interleave(N, 2)
:par(i, N, -);
// Input distributor: mono in → 8 parallel tapsdistrib=_<:si.bus(N);
// Close the loop with ~ ; sum 8 outputs and scale.fdn8=distrib: (si.bus(N) :>si.bus(N)) : (delays:mix) ~si.bus(N)
: (si.bus(N) :>*(1.0/N));
process=fdn8;
Interactive: FDN reverb tail
Top: time-domain response of a 4-delay miniature FDN (scaled down from 8 for visual clarity). Bottom: log-envelope showing exponential decay governed by g.
9Physical Modelling in Faust
Karplus-Strong (1983) and Julius Smith's digital waveguide formalism (1987)
are compact enough to compile in a browser and expressive enough to make
convincing plucked-string and bowed-string sounds. Faust's
physmodels.lib contains dozens of these models; here we rebuild
the canonical three.
9.1 Karplus-Strong, three lines
import("stdfaust.lib");
// Excite once, then a delay line with in-line 2-tap lowpass in feedback.ks(freq, decay) =noise:+~ (@(N) :avg:*(decay))
with {
N=int(ma.SR/freq) -1;
avg=_<:_,mem:>*(0.5);
noise=no.noise* (ba.time<N); // one-period burst only
};
process=ks(220, 0.998);
The two-tap average _ <: _, mem :> *(0.5) is a symmetric FIR
lowpass with zero at Nyquist, which is both a simple frequency-dependent
loss and a half-sample delay — the original trick of Karplus & Strong's
1983 paper.
9.2 Bowed-string core loop
A digital waveguide consists of two bidirectional delay lines (the two
travelling waves on an ideal string) plus junction boxes at each end. Julius
Smith's bowed-string model (PASP, Ch. 9) adds a friction nonlinearity
at the bow contact point:
Top: first 2048 samples of the pluck. Bottom: log-magnitude envelope on a dB scale — the straight-line decay is the characteristic audible signature of KS.
Faust is a compiler front-end. It produces a language-agnostic internal
representation, then hands it to a backend which emits C++, Rust, LLVM IR,
WebAssembly, JavaScript, Julia, JSFX, Cmajor or CUDA. The faust2…
shell-script family wraps that compiler with platform-specific glue so one
source file can become a standalone GUI app, a VST plug-in, a JUCE project,
a Pure Data external, or a web-audio node.
The useful subset of the faust2… scripts
Script
What it produces
Typical use
faust2webaudio
an ES-module WebAudio node (WASM + glue JS)
deploy a plug-in to a web page
faust2jaqt
a standalone Qt app with JACK I/O
prototype on Linux
faust2vst
a VST2 or VST3 plug-in binary
run inside a DAW
faust2juce
a full JUCE project directory
ship a cross-platform plug-in
faust2pd
a Pure Data external (.pd_linux, .dll, .pd_darwin)
embed in a Pd patch
faust2android
an .apk with a generated Android UI
run on a phone
faust2ios
an Xcode project for iOS
run on iPad / iPhone
faust2sndfile
a command-line .wav → .wav offline renderer
batch-process audio
The Faust IDE
Since 2018 the entire compiler has run in the browser via
faustide.grame.fr. You paste
a .dsp file into the left pane; the IDE compiles to WASM, spawns
a Web Audio node, draws the block diagram, and gives you sliders that are
live-linked to the hslider declarations in the source. Every listing
in this guide runs there unmodified.
The standard libraries
Faust ships with a collection of .lib files under
libraries/. They are themselves Faust programs — open
filters.lib and you can read exactly how fi.resonlp is
implemented. Every listing below uses the prefix namespace style: import
stdfaust.lib once and refer to definitions as os.osc,
fi.lowpass, de.fdelay, etc.
Left: compressor static curve (input dB vs output dB) with soft-knee transition at the threshold. Right: three-band EQ magnitude response computed live from the slider values.
11.2 A complete FreeVerb in Faust
FreeVerb (Jezar 2000) is a Schroeder-style reverb: eight lowpass-feedback
comb filters in parallel driving four Schroeder allpasses in series. It sits
in reverbs.lib as re.mono_freeverb:
11.3 A nonlinear high-shelf — tube-style brightness
Classic tube preamps brighten the signal more at high input levels. We
emulate this by a soft-clipper after a high-shelf, with the shelf gain
modulated by the envelope follower.
import("stdfaust.lib");
drive=hslider("Drive [unit:dB]", 6, 0, 24, 0.1) :ba.db2linear:si.smoo;
soft(x) =x/ (1.0+abs(x));
// env-modulated high-shelf: up to +8 dB at full driveenvShelf=_<:an.amp_follower(0.01) :*(8.0) ,_:fi.highshelf(2, _, 3500);
process=*(drive) :envShelf:soft;
The soft function above is Schlichting's $x/(1+|x|)$ soft clipper —
odd-order only, so perfectly symmetric. Replace by $\tanh$ for a slightly
warmer curve or $x - x^3/3$ for a polynomial one that is cheap to
differentiate (useful for neural-Faust work in Ch 12).
12Legacy and Continuing Impact
Twenty-plus years after Orlarey, Fober and Letz's first ICMC paper, Faust
has become the lingua franca of research-grade audio DSP. Its role in
the modern ecosystem is threefold: the browser-audio bridge, the reference
plug-in generator, and the teaching language of choice at Stanford, IRCAM,
KAIST and a dozen other labs.
Real-time WASM audio
The earliest demonstration that Faust could target the browser was a 2015
Grame paper by Letz, Denoual and Orlarey; today
faust2webaudio produces a drop-in ES-module Audio Worklet that
runs inside the browser's real-time audio thread. Most major interactive
DSP demonstrations on the modern web — including faust-playground,
mephisto.js, and several of the Cycling '74 RNBO examples — are
built on this pipeline.
Plug-in generation
Commercial plug-in houses (e.g. Kadenze, Kilohearts, several smaller
companies) use faust2juce as a prototyping backend: a DSP engineer
writes the algorithm in Faust, generates a JUCE project, then iterates on
the GUI and host integration in C++. The Faust source becomes the
canonical specification of the algorithm.
Teaching
Julius Smith's Audio Signal Processing in Faust is the formal
companion textbook to Stanford's Music 220 and 320C. Romain Michon's
Master's-level course at CCRMA is entirely Faust-based; Grame itself runs
annual summer schools. The low syntactic overhead of Faust means an entire
biquad appears on one slide, which is an educational property no other DSP
language shares.
Differentiable audio-DSP
Because every Faust program is a single expression with no mutable state,
automatic differentiation is comparatively straightforward. Recent work
(Chowdhury & Clarke 2022; Hayes et al. 2023) compiles Faust programs to
PyTorch or JAX back-ends for gradient-based optimisation — so you can
fit the parameters of a hand-designed Faust reverb or compressor to audio
data in the same way you train a neural network. The
DDSP line of
research (Engel et al. 2020) took this idea to a full generative model;
several follow-ups have re-expressed their backbones in Faust to recover
interpretability.