Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Faust

Faust (Functional Audio Stream) is a domain-specific language for audio signal processing developed at GRAME (Centre National de Création Musicale) in Lyon, starting around 2002. It occupies a unique position in the audio programming landscape: like Csound it is text-based and mathematically expressive, but like hvcc/Pure Data it compiles to self-contained C/C++ with no runtime - making it deployable on everything from web browsers to ARM Cortex-M4 microcontrollers.

The central idea is block diagram algebra: you describe a signal processor as a composition of simpler processors using a small set of operators, and the compiler works out the efficient C implementation automatically.

Installation

sudo apt install faust                  # Debian/Ubuntu
brew install faust                      # macOS

# Or get the latest with all tools
git clone https://github.com/grame-cncm/faust.git
cd faust && make && sudo make install

The online IDE requires no installation: faustide.grame.fr

Core concept: block diagram algebra

In Faust, everything is a block - a function that takes N input signals and produces M output signals. Blocks are composed with five operators:

OperatorSymbolMeaningExample
Sequential:Output of A feeds input of BA : B
Parallel,A and B run side by sideA , B
Split<:One output fans out to many inputsA <: B , C
Merge:>Many outputs sum into fewer inputsA , B :> C
Recursive~Feed output of B back to input of AA ~ B

The entire language is built from these five operators plus a set of primitives. There are no loops, no mutable state - feedback is expressed explicitly with ~.

Minimal example

process = _;   // identity: one input, one output (wire)
process = !;   // cut: discard input, produce nothing
process = 0;   // constant: produce the value 0

A sine oscillator at 440 Hz:

import("stdfaust.lib");

process = os.osc(440);

Stereo output (duplicate the mono signal):

import("stdfaust.lib");

process = os.osc(440) <: _, _;   // split to left and right

Primitives

Math

+, -, *, /          // arithmetic (infix: two inputs → one output)
%                   // modulo
^                   // power
&, |, xor          // bitwise
<<, >>              // bit shift
<, <=, >, >=, ==, !=  // comparison (output: 0 or 1)
abs, floor, ceil, round
sin, cos, tan, asin, acos, atan, atan2
exp, log, log10, sqrt, pow
min, max

Signal primitives

_           // identity (wire)
!           // cut (terminate a signal)
mem         // one-sample delay
@           // variable delay: x @ d  reads x delayed by d samples
rdtable     // read a table
rwtable     // read/write table
select2     // if/else: select2(cond, a, b)
select3     // three-way selector

Conversions

ba.samp2sec(n)      // samples to seconds
ba.sec2samp(s)      // seconds to samples
ma.SR               // current sample rate
ma.T                // sample period (1/SR)

UI elements

UI elements define parameters that the host (DAW, hardware, web app) can control. They produce a signal - a stream of values reflecting the current setting.

hslider("name", default, min, max, step)   // horizontal slider
vslider("name", default, min, max, step)   // vertical slider
nentry("name", default, min, max, step)    // numeric entry
button("name")                             // momentary (0 or 1)
checkbox("name")                           // toggle (0 or 1)

Grouping (for UI layout):

hgroup("Group Name", ...)    // horizontal group
vgroup("Group Name", ...)    // vertical group
tgroup("Tab Name", ...)      // tabbed group

Example - a tunable oscillator with volume:

import("stdfaust.lib");

freq = hslider("freq [unit:Hz]", 440, 20, 20000, 0.1);
gain = hslider("gain", 0.5, 0, 1, 0.01);

process = os.osc(freq) * gain <: _, _;

The standard library

stdfaust.lib imports all standard libraries. Key namespaces:

NamespaceContents
osOscillators: osc, sawtooth, square, triangle, phasor
fiFilters: lowpass, highpass, bandpass, peak_eq, resonlp
efEffects: echo, chorus, flanger, freeverb, zita_rev1
enEnvelopes: adsr, asr, ar, smoothEnvelope
noNoise: noise, pink
baBasic utilities: if, sAndH, impulsify, db2linear
maMath constants and functions: SR, PI, EPSILON
roRouting: interleave, cross, hadamard
siSignal utils: smoo, smooth, bus, block
pmPhysical models: strings, waveguides
spSpatialization
dmDemo versions of effects with built-in UI

Common patterns

Envelope

import("stdfaust.lib");

gate   = button("gate");
attack = hslider("attack",  0.01, 0.001, 2, 0.001);
decay  = hslider("decay",   0.1,  0.001, 2, 0.001);
sustain= hslider("sustain", 0.8,  0,     1, 0.01);
release= hslider("release", 0.2,  0.001, 4, 0.001);

env    = en.adsr(attack, decay, sustain, release, gate);
process = os.osc(440) * env <: _, _;

Filter

import("stdfaust.lib");

cutoff = hslider("cutoff [unit:Hz]", 1000, 20, 20000, 1);
res    = hslider("resonance", 0.5, 0, 1, 0.01);

// Noise through a resonant low-pass filter
process = no.noise : fi.resonlp(cutoff, res, 1) <: _, _;

Feedback delay (recursive)

import("stdfaust.lib");

// feedback: output is delayed and added back to input
// ~ is the recursive operator
delay_samples = int(hslider("delay [unit:ms]", 250, 1, 2000, 1) * ma.SR / 1000);
feedback      = hslider("feedback", 0.5, 0, 0.99, 0.01);

echo = _ <: _, (@ (delay_samples) * feedback) :> _;
process = echo <: _, _;

FM synthesis

import("stdfaust.lib");

freq  = hslider("freq",  440, 20, 8000, 0.1);
ratio = hslider("ratio", 2,   0.1, 10,  0.01);
depth = hslider("depth", 200, 0,   5000, 1);

modulator = os.osc(freq * ratio) * depth;
carrier   = os.osc(freq + modulator);

process = carrier * 0.5 <: _, _;

Polyphony

Faust supports built-in polyphony via special parameter names. Declare freq, gain, and gate and compile with -nvoices N:

import("stdfaust.lib");

freq = hslider("freq",   440, 20, 20000, 0.1);
gain = hslider("gain",   0.5, 0,  1,     0.01);
gate = button("gate");

env  = en.adsr(0.01, 0.1, 0.8, 0.2, gate);
process = os.sawtooth(freq) * env * gain <: _, _;
faust2juce -nvoices 8 mysynth.dsp   # 8-voice polyphonic VST/AU

Compilation targets

Faust compiles via architecture files - wrappers that connect the generated DSP code to a specific host API. The faust2* scripts bundle generation + compilation:

CommandOutput
faust2c myfile.dspStandalone C file
faust2juce myfile.dspJUCE project (VST/AU/standalone)
faust2vst myfile.dspVST2 plugin
faust2lv2 myfile.dspLV2 plugin (Linux)
faust2jack myfile.dspJACK audio application
faust2alsa myfile.dspALSA application
faust2webaudio myfile.dspWeb Audio API (JavaScript)
faust2wasm myfile.dspWebAssembly module
faust2pd myfile.dspPure Data external
faust2logue myfile.dspKORG Logue SDK unit
faust2teensy myfile.dspTeensy audio library

Logue SDK (NTS-1 / minilogue xd / prologue)

Faust compiles directly to Logue oscillator units - no intermediate step required (unlike the Pd→hvcc→Logue pipeline):

# Install dependencies
sudo apt install faust gcc-arm-none-eabi

# Clone logue-sdk alongside your Faust file
git clone https://github.com/korginc/logue-sdk.git

# Compile for NTS-1 (nutekt-digital)
faust2logue -nvoices 1 myosc.dsp

# Output: myosc.ntkdigunit  (or .prlgunit / .mnlgxdunit)

The faust2logue script handles:

  • ARM Cortex-M4 compilation (-mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard)
  • Memory layout within the 32 KB SRAM constraint
  • Mapping Faust UI sliders → Logue knob parameters
  • Wrapping the DSP code in OSC_INIT, OSC_CYCLE, OSC_PARAM

Parameter mapping: Faust sliders named freq, gain, gate are automatically bound to the corresponding Logue signals; other sliders become assignable parameters.

import("stdfaust.lib");

// These names are special - bound automatically by faust2logue
freq = nentry("freq", 440, 20, 20000, 1);
gain = nentry("gain", 0.5, 0, 1, 0.01);

// Custom parameters appear as knobs in the Logue menu
shape = hslider("shape", 0, 0, 1, 0.01);

osc = os.sawtooth(freq) * (1 - shape) + os.osc(freq) * shape;

process = osc * gain;

Comparing Faust, Csound, and Pure Data

FaustCsoundPure Data
ParadigmFunctional / algebraicOrchestra + scoreVisual dataflow
InterfaceTextTextGraphical
OutputCompiled C - no runtimeInterpreter + runtimeInterpreted (or via hvcc)
Embedded targetsYes (Logue, Teensy, bare-metal)No (runtime too large)Yes (via hvcc)
Rate systemImplicit (compiler infers)Explicit (i/k/a-rate)Implicit (control vs ~)
FeedbackExplicit with ~Implicit in instrumentPatch cords with ~
PolyphonyBuilt-in (-nvoices N)Instruments + scoreManual (via poly~)
Library sizeLarge standard library1500+ opcodesSmall core + externals
Live patchingNo (recompile)LimitedYes
Learning curveAlgebraic thinking requiredSteepVisual, intuitive

Faust and Csound share a mathematical, declarative approach - if you are comfortable with Csound’s signal flow thinking, Faust will feel familiar. The key difference is that Faust’s block diagram model is more restrictive (everything must be statically composable) but that restriction is what allows the compiler to produce efficient, allocation-free code.

Resources