faust2clap: Faust DSP to CLAP Plugin Framework
- faust2clap is a framework that compiles Faust DSP specifications into CLAP plugins, offering both ahead-of-time native binary generation and runtime interpretation.
- The system supports dual-mode operation, enabling efficient deployment in static mode and rapid development in dynamic mode via live reload.
- It employs address-based parameter matching and stable slot allocation to ensure that host automation and parameter values persist through source-level edits.
Searching arXiv for the specified paper to ground the article in the cited source.
faust2clap is a framework that establishes the first officially maintained compilation pathway from Faust DSP specifications to the CLAP format, with a dual-mode design that combines ahead-of-time native code generation and runtime interpretation (Franchino et al., 11 Jun 2026). In static mode, Faust source is compiled into a platform-native .clap shared library with zero runtime interpretation overhead. In dynamic mode, the DSP is interpreted through libfaust and can be rebuilt in place after source modification without restarting the host application, thereby addressing the cumulative overhead of the edit, compile, and reload cycle. The central technical problem treated by the system is parameter identity across structural DSP mutations: faust2clap introduces address-based identity matching and a stable slot allocation scheme so that parameter values and host automation bindings can survive source-level edits.
1. Placement within the Faust and CLAP toolchain
The framework is organized around two explicit goals: providing both an ahead-of-time and a runtime compilation pathway from Faust DSP descriptions to CLAP plugins, and preserving host automation and parameter values across source-level edits through semantic, address-based identity matching (Franchino et al., 11 Jun 2026). This situates faust2clap at the interface between Faust, which specifies DSP structure, and CLAP, which supplies the plugin ABI and host-facing parameter model.
The high-level architecture is split into two pipelines. In static mode, the path is:
Faust DSP faust2clap.py (Python orchestrator) clap-arch.cpp (C++ adapter) CMake build plugin.clap (native binary).
In dynamic mode, the path is:
Faust DSP efsw file watcher libfaust interpreter VM bytecode real-time audio processing.
This division is significant because it separates two normally conflicting objectives. Static compilation prioritizes deployment efficiency and native execution. Dynamic interpretation prioritizes iterative development by allowing hot reload. A plausible implication is that faust2clap is intended not as a single compilation strategy, but as a coordinated development-and-deployment workflow in which the same Faust source can move between exploratory and production-oriented execution contexts.
2. Dual-mode compilation model
The static mode is an ahead-of-time compilation pathway. The user runs faust2clap.py, supplying the DSP .dsp source and target metadata; the script invokes the Faust compiler with the special clap-arch.cpp architecture file; Faust emits a C++ “glue” file implementing the Faust DSP interface; CMake then compiles the glue code, links against the CLAP SDK, and produces a fully native plugin binary (Franchino et al., 11 Jun 2026). The resulting output is a platform-native .clap shared library with full multi-threaded support per the CLAP threading model.
The dynamic mode uses runtime interpretation instead of native code generation. It employs libfaust’s interpreter backend to compile the DSP function into a stack-based VM bytecode. A dedicated file-watcher thread based on efsw monitors the DSP source file, and when the source changes the system rebuilds the VM bytecode in place. No process or host restart is required.
The distinction is not merely implementation-level. The static path is explicitly optimized through Faust’s standard SSA-based algebraic optimisations, including inlining, loop unrolling for filters, and constant-folding, together with C++ compiler optimisations such as -O3 and vectorisation when available. The dynamic path instead prioritizes mutability and development cadence. This suggests a deliberate separation between a deployment backend and a live-edit backend, rather than an attempt to make interpretation indistinguishable from native execution in all respects.
3. Static compilation pathway and native plugin generation
The static pathway begins with faust2clap.py, which functions as a Python CLI for metadata handling and CMake invocation. It delegates DSP compilation to Faust using the clap-arch.cpp architecture file, after which CMake compiles and links the generated glue code against the CLAP SDK (Franchino et al., 11 Jun 2026). clap-arch.cpp implements Faust’s dsp interface on top of CLAP, including parameter registration, audio callbacks, and state load/store.
Several optimization layers are explicitly identified. At the Faust level, the system uses standard SSA-based algebraic optimisations, including inlining, loop unrolling for filters, and constant-folding. At the C++ compilation level, it relies on compiler optimisations such as -O3 and vectorisation when available. Polyphonic support is provided via the mydsp_poly wrapper, with 16 voices as the default for instruments.
The consequence is a native .clap binary with zero runtime interpretation overhead. In practical terms, static mode is the pathway intended for maximal execution efficiency and full CLAP-native behavior. The presence of polyphonic support only in static mode also establishes an asymmetry between the two backends: the static backend is the mature target for instrument-style deployment, whereas the dynamic backend is optimized for editability rather than full feature parity.
4. Dynamic interpretation, live reload, and runtime behavior
The dynamic pathway compiles the Faust DSP function into stack-based VM bytecode through the libfaust interpreter backend (Franchino et al., 11 Jun 2026). The implementation uses separate memory heaps for integers and floats to guarantee deterministic state across reloads. A file-watcher thread marks the DSP as dirty when the source file changes. On the next audio callback, the audio thread observes the atomic dirty flag, pauses processing briefly, rebuilds the VM bytecode in place, and then resumes processing on the new VM instance while preserving parameter values and slots.
The live-reload sequence is specified as a three-step procedure. First, the file watcher sets an atomic dirty flag on source modification. Second, on the next audio callback, the audio thread sees the flag, pauses processing briefly for 5–60 ms, and rebuilds the VM bytecode in place. Third, audio resumes on the new VM instance, preserving parameter values and slots. Host buffer conversion between 32-bit and 64-bit floats is handled transparently.
Evaluation data were reported on an Apple M2 running macOS 26.3 in REAPER 7.0. Reload latency measurements in dynamic mode were 6 ms for Gain (1 param), 13 ms for Filter (3 params), 12 ms for Oscillator (2 params), 6 ms for Delay (4 params), and 52 ms for Reverb (12 params); all reloads were below 60 ms. Interpreter throughput per block at 256 samples and 48 kHz was 0.008 ms for a resonant low-pass filter, 0.010 ms for a sine oscillator, 0.009 ms for a feedback delay, and 0.27 ms for dm.zita reverb. Even the most complex tested DSP used only 5% of a block budget, leaving more than headroom for polyphony or additional instances.
These measurements define the scope of the dynamic backend precisely. It supports hot reload without restarting the host, but not uninterrupted mutation: the rebuild introduces a brief audio discontinuity. A common misunderstanding would be to treat dynamic mode as a seamless real-time code-swapping system; the reported behavior is instead development-oriented hot reload with an acceptable interruption window.
5. Parameter identity across structural DSP mutation
A core problem addressed by faust2clap is parameter identity when the DSP’s control graph changes because parameters are added, removed, or renamed (Franchino et al., 11 Jun 2026). The requirement is twofold: preserving numerical parameter values across reloads, and preserving the association between host automation and the corresponding DSP control. The system addresses this through address-based identity matching.
Let be the old DSP instance with parameter count 0, and 1 the new instance with parameter count 2. Each parameter index 3 in 4 has an address string
5
and a value
6
The algorithm constructs a hash map
7
in 8 time. It then traverses the parameters of 9 and, for each new parameter 0, performs address lookup and restoration:
1
The total complexity is 2.
The associated pseudocode is conceptually straightforward: build an old address-to-value map from 3, then scan the parameters of 4 and restore any parameter whose address is present in the map, subject to clamping by the new parameter bounds. The significance of this design lies in its semantic criterion for identity. Parameter continuity is not derived from positional index stability, but from address stability. This makes the method robust to structural edits that would invalidate naive index-based remapping.
6. Stable slot allocation, implementation, and development trajectory
CLAP requires parameter IDs to remain stable across reloads if host automation is to remain bound to the intended controls (Franchino et al., 11 Jun 2026). faust2clap addresses this with a stable slot allocation scheme built on a fixed-size array Slots[0…k−1], where 5. Each slot holds a slotID exposed to the host, a boundAddress string, and a boundIndex, which is the current parameter index in the Faust DSP or null if the slot is free.
Allocation proceeds in two passes. In the first pass, existing bindings are preserved: for each slot, if its boundAddress appears among the new DSP addresses at index 6, the slot’s boundIndex is updated to 7; otherwise the slot is marked free. In the second pass, each new parameter address not already bound in any slot is assigned to the first free slot. Parameters beyond the fixed slot count are processed internally but are not exposed to host automation. Because 8 is constant at 12, the overall complexity is 9, and the scheme guarantees that if a parameter’s address survives a reload, its host-exposed slotID remains identical.
Empirically, in all tested reloads where addresses remained unchanged, both value preservation and slot persistence were successful. This is the operational basis for maintaining host automation continuity under hot reload. The mechanism also imposes a clear boundary: persistence is guaranteed for surviving addresses, not for arbitrary structural transformation.
The implementation comprises approximately 2,400 lines of C++ architecture and Python tooling code. Its principal components are faust2clap.py, clap-arch.cpp, file_watcher.cpp/.h, and vm_interpreter.cpp/.h. The stated dependencies are the Faust compiler and libfaust interpreter, the CLAP SDK, CMake, and efsw; static mode additionally requires a C++ compiler such as Clang, GCC, or MSVC. The system has been integrated into the main Faust distribution, and source code is available at https://github.com/cucuwritescode/faust2clap.
The stated contributions are the first officially maintained Faust-to-CLAP compiler backend, the dual-mode static and dynamic design, and the combined use of address-based matching with stable slot allocation to preserve automation across structural DSP edits. The current limitations are equally explicit: dynamic mode induces a brief 5–60 ms audio discontinuity during code rebuild, is currently monophonic, and at present is available only on macOS. The future directions proposed are an LLVM-based JIT backend to reduce rebuild latency and eliminate discontinuities, double-buffered DSP instances so rebuild occurs off the audio thread, per-voice state management for true polyphonic hot reload, and Linux and Windows support for the file-watcher and dynamic loader. Together, these indicate that faust2clap is already positioned as a maintained backend, while its dynamic mode remains an actively evolving subsystem rather than a finalized cross-platform execution model.