danzig
A VST3 plugin framework written in pure Zig. No JUCE. No Steinberg SDK. No C++ at all in the core.
Source: github.com/godofecht/danzig
What danzig is
VST3 is a C ABI dressed up as COM. A plugin is a shared library exporting one
symbol, GetPluginFactory. The host calls it, reads the first machine word of
the returned pointer as a vtable pointer, and calls through that vtable to
discover classes, create objects, and push audio buffers.
That contract is small. It is also the only part of Steinberg's SDK a plugin strictly needs. Everything else in the SDK is C++ scaffolding around it.
danzig implements the contract directly in Zig. src/vst3.zig declares the
interfaces as extern structs of callconv(.c) function pointers, which is
exactly what a C++ vtable is at the machine level. A plugin fills in the
function pointers and returns a pointer to the struct. The host cannot tell the
difference.
The reasons to do this rather than use JUCE:
Fast builds. A clean build of the library, five example binaries, and both architectures of the plugin takes about 5.6 seconds on an M-series Mac. A no-change rebuild takes 0.6 seconds, and packaging the universal bundle on top of a warm cache takes 0.5 seconds. There is no CMake step and no dependency tree.
One binary format decision, made explicitly. The bundle layout, the
Info.plist, and the lipo invocation are twenty lines of build.zig you can
read. Nothing is hidden behind a framework's packaging step.
Allocation is visible. Zig has no hidden allocations and no destructors that
run at surprising times. On the audio thread that matters. The parameter store
in src/params.zig is a fixed array of atomics with no heap involvement at all,
which you can verify by reading 160 lines.
Cross-compilation is free. Zig builds x86_64-macos from an arm64 machine
with no extra toolchain. That is what makes the universal bundle a build step
rather than a CI matrix: build.zig compiles the plugin for both architectures
and merges them with lipo, on whichever machine you happen to be on.
macOS is the only supported platform today. The VST3 bundle layout, the
install-vst3 step, and the GUI example are all macOS-specific. The library,
the unit tests, and the command-line examples are portable Zig and should build
anywhere Zig runs, though only macOS is tested.
Current state
danzig builds a VST3 plugin that a host loads, instantiates, and validates.
Working and tested.
- The core library:
vst3.zig,plugin.zig,audio.zig,params.zig. - 35 unit tests covering dB conversion, ramps, buffers, and the atomic parameter store.
- An integration harness that links the built plugin, calls its exported
GetPluginFactory, and drives the returned object through the raw C ABI. - A universal arm64 + x86_64
.vst3bundle that installs into the macOS plugin folder and is ad-hoc signed by the linker. - Three runnable non-plugin examples: an offline WAV processor, an HTTP server serving the web UI, and a native window with an embedded WebView and CoreAudio device enumeration.
The plugin validates.
The factory in examples/danzig-gain is complete. getClassInfo returns a
populated PClassInfo, and createInstance builds one object exposing
IComponent, IAudioProcessor, and IEditController over a shared lock-free
parameter store. A host scans the bundle, finds the class, and instantiates it.
pluginval passes at strictness level 5:
/Applications/pluginval.app/Contents/MacOS/pluginval \
--validate zig-out/DanzigGain.vst3 --strictness-level 5 --timeout-ms 30000
Starting tests in: pluginval / Scan for plugins located in: .../DanzigGain.vst3...
Num plugins found: 1
Testing plugin: VST3-Danzig Gain-...
Superelectric: Danzig Gain v0.1.0
...
SUCCESS
Nineteen test groups run, from instantiation and bus layouts through parameter automation and state round-trips, with no failures. So danzig loads in a DAW as a working gain plugin, and doubles as a DSP and parameter library with a validated VST3 build pipeline.