A language for audio, with a compiler that ships nothing.
Pole compiles a DSP patch to an object file and a C header. Link them and there is no JIT, no runtime and no compiler inside the plugin you ship. The same compiler targets WebAssembly — which is why the patch below plays in this page.
processor Pluck
{
input stream float pitch;
input stream float gate;
output stream float out;
param float decay = 0.4f [0.05f, 2.0f];
param float tone = 0.4f [0.05f, 0.95f];
float phase = 0.0f;
float env = 0.0f;
float lp = 0.0f;
void main()
{
loop
{
if (gate > 0.5f) { env = 1.0f; }
else { env = env * (1.0f - 1.0f / (decay * 48000.0f)); }
let saw = phase * 2.0f - 1.0f;
lp = lp + tone * (saw - lp);
out <- lp * env * 0.3f;
phase = phase + pitch / 48000.0f;
if (phase >= 1.0f) { phase = phase - 1.0f; }
advance();
}
}
}What it compiles to
examples/reverb.pole, compiled for a plugin and for a browser. The sizes are of the files polec writes.Native
An object file and a C header. The header says how big the state block is; Pole never allocates. Link it into a plugin and the plugin carries no compiler.
WebAssembly
An object and a JSON manifest that tells a browser everything the C header would have told C. Nothing on that side parses the header — a runtime that parsed C would break when its formatting changed.
The same code
The exported object is the optimised module a JIT session would have run, so an exported plugin is not a port of what you were hearing. A bit-identity gate holds it to that.
Measured, not estimated
Every figure here was produced by running the thing that produces it, on the commit this page was written from.
polec --emit=export on examples/reverb.pole. The header beside it is 4.3 KB.
_expf. That is the whole run-time dependency: no compiler, no runtime, libm.
Median of eleven runs each for gain, filter, synth and reverb, to an object and a header.
- adsr
- bounded_search
- delay
- filter
- fuzz
- gain
- modmatrix
- octave_up
- poly_graph
- poly_simple
- poly_synth
- poly_voices
- reverb
- saw_gate
- shaper
- sine
- soft_clip
- stereo_chorus
- stereo_width
- synth
- wavefolder
- wavetable
- wt_load
- wt_synth
- fft93 of 4,096 samples differ, by −150.5 dBFS at most
- fx_chain2 of 4,096 samples differ, by −156.5 dBFS at most
- spectral923 of 4,096 samples differ, by −138.5 dBFS at most
- wt_osc532 of 4,096 samples differ, by −138.5 dBFS at most
- feedback_lfoa feedback graph, which cannot run in a browser yet
- poly_eventsneeds notes, which the check does not play
scripts/check_wasm.py. The four that differ all call sin, cos, exp, pow or log, which the compiler takes from macOS’s maths library and the browser from musl’s.Where it runs
| macOS | Linux | Windows | |
|---|---|---|---|
| compile, render, export | verified | verified | not built |
| WebAssembly output | verified | verified | not built |
| live audio and MIDI | verified | no backend | no backend |
“Not built” means exactly that: the compiler core has no platform headers and no reason not to build on Windows, but nobody has built it, so this table does not say it works. The platforms page has the detail.
Start anywhere
Write a patch in the browser
The playground compiles Pole with the same compiler and plays it in the page. There is nothing to install.
Open the playground