Skip to content
  1. Guides

Custom DSP (Faust)

Voro ships a power-user authoring path: write a Faust .dsp, run one command, and get a self-contained Voro DSP operator that loads in TouchDesigner alongside the built-in operators. The SDK lives in the package’s voro_dsp_sdk/ folder, and the operators you build land in OP Create → VOP family → Custom group.

This authoring path is preview and unstable. The plugin you build today may not load in a future Voro engine, and the usage and licensing terms are still being finalized. Treat it as something to experiment with, not a stable platform to ship against yet. The full caveat is in voro_dsp_sdk/README.md.

This is ahead-of-time (AOT) compilation, not a live, in-TouchDesigner JIT. You compile a .dsp to a DLL on the command line once, then drop it into the package. There is no runtime code compilation inside the engine.

The result is a real Voro operator: it cooks off the frame clock, exposes a parameter page built from your Faust controls, and wires into a patch like any other audio operator.

  • Faust 2.85.5 or newer (default install at C:\Program Files\Faust).
  • Visual Studio 2022 Build Tools with the MSVC x64 compiler.
  • Windows for this Faust build toolchain. The Voro engine itself also runs on macOS (Apple Silicon); the custom-DSP build flow here is documented for Windows.

Inside voro_dsp_sdk/:

  • examples/ — reference DSPs: a sine synth, a mono drive, a quad generator, and a stereo low-pass filter.
  • tools/faust2voro.ps1 (and a .sh variant) — the one-command build.
  • validator/ — a standalone loader that runs your plugin’s audio processing with no TouchDesigner and no engine, to confirm it built correctly.
  • sdk/ — the architecture file and headers your plugin compiles against.
  • README.md — the authoritative reference, including the lower-level details this page intentionally skips.

Operator identity comes from Faust declare metadata; parameters come from your control labels.

declare name "my_fx_v1";
declare author "you";
declare version "0.1.0";
import("stdfaust.lib");
cutoff = hslider("Cutoff [unit:Hz]", 800, 40, 18000, 1) : si.smoo;
process = fi.resonlp(cutoff, 1.0, 1.0), fi.resonlp(cutoff, 1.0, 1.0);
  • declare name becomes the operator’s canonical id (sanitized).
  • Each hslider / vslider leaf label becomes a Voro parameter name.
  • The process arity sets the input and output channel counts (the example above is stereo in, stereo out).
.\tools\faust2voro.ps1 -Dsp .\examples\voro_stereo_lpf.dsp

This produces out\<name>.dll plus an out\<name>.voroplugin.json manifest (name, version, author, license, source hash, and the preview-unstable flag).

3. (Optional) Validate without TouchDesigner

Section titled “3. (Optional) Validate without TouchDesigner”
cl /nologo /O2 /EHsc validator\voro_validator.cpp /Isdk\include /Fe:voro_validator.exe
.\voro_validator.exe out\<name>.dll

An exit code of 0 means the plugin loaded and its audio processing passed the structural checks — a fast way to confirm a build before bringing it into TouchDesigner.

  1. Copy <name>.dll into the package’s plugins/custom/ folder.
  2. In TouchDesigner, run the package’s Refresh to rescan custom plugins.
  3. Your operator appears in OP Create → VOP family → Custom group.

Place it and wire it like any other operator. Its parameter page is built from your Faust controls.

Note: TouchDesigner keeps a loaded plugin DLL locked. Build to a fresh name or close TouchDesigner before replacing a DLL that is already in use.

Faust architecture files carry a GPL-with-linking-exception, so the plugin you generate is yours to license. Each .voroplugin.json records the license fields. See voro_dsp_sdk/README.md for the details, and watch that file for the finalized preview terms.

  • Overview — the operator groups and how a patch flows.
  • Audio FX Chain — how an audio effect sits in a signal path, which is exactly where most authored effects land.