2026-08-21 13:08:18 +09:00
# NIH-plug Template
2026-08-21 03:55:36 +00:00
2026-08-21 13:08:18 +09:00
Minimal scaffold for new [NIH-plug ](https://github.com/robbert-vdh/nih-plug ) audio plugins (Rust + CLAP/VST3 + egui).
Based on patterns from [`musiikki/codename-206` ](http://192.168.2.199:3005/musiikki/codename-206 ).
## Quick Start
``` bash
# Clone this repo as your starting point
git clone http://192.168.2.199:3005/musiikki/nih-plug-template my-plugin
cd my-plugin
# Edit: src/lib.rs, Cargo.toml, bundler.toml — replace placeholder values
# See musiikki/codename-206 for a real-world example of how to fill these in
2026-08-29 10:24:52 +09:00
# Then verify the rename landed everywhere
sh check-branding.sh
2026-08-21 13:08:18 +09:00
```
2026-08-29 10:24:52 +09:00
## Renaming
2026-08-31 23:11:47 +09:00
Identity is duplicated across `Cargo.toml` , `bundler.toml` , `src/lib.rs` , `NOTICE` ,
`Cargo.lock` , the CI workflow and the docs. Nothing makes those agree, so a half-finished
rename builds a bundle under one name while CI bundles another.
2026-08-29 10:24:52 +09:00
2026-08-31 23:11:47 +09:00
`branding.conf` holds the canonical identity once. Two scripts read it:
2026-08-29 10:24:52 +09:00
``` bash
2026-08-31 23:11:47 +09:00
sh brand-plugin.sh --display "Fuzz Box" # derive, validate, show the plan (dry run)
sh brand-plugin.sh --display "Fuzz Box" --apply
sh check-branding.sh # exit 0 = consistent, 1 = drift
2026-08-29 10:24:52 +09:00
```
2026-08-31 23:11:47 +09:00
* * `brand-plugin.sh` changes names.** It derives the package name and Rust types from the
display name, validates them against what Cargo actually accepts, rewrites every tracked
file, appends the retired spellings to `STALE` , and finishes by running the checker — a run
that ends green is a rename that landed. It is a dry run unless you pass `--apply` .
2026-08-29 10:29:06 +09:00
2026-08-31 23:11:47 +09:00
* * `check-branding.sh` verifies them.** Both directions: every canonical value is asserted
present where it belongs, * and * no retired spelling survives anywhere. Absence alone is not
enough — renaming to the wrong new spelling would pass a stale-sweep and still break CI.
2026-08-29 10:24:52 +09:00
2026-08-31 23:11:47 +09:00
Three things it will refuse or warn about, all learned the hard way:
- **A package name Cargo rejects.** Names cannot start with a digit (`206 Prototype` →
`206_prototype` , rejected), be a Rust keyword, or collide with a built-in crate.
- **Moving frozen identity.** `CLAP_ID` and `VST3_CLASS_ID` are how hosts recognise saved
projects and presets. Change either and every existing session silently loses the plugin —
the track comes up empty, with no error. Pick them once before the first release; after
that they never move. `--allow-identity-change` is required to touch them, and the helper
proposes a valid 16-byte class ID rather than deriving one, because that choice is yours.
- **Development history in `CLAP_DESCRIPTION` .** Hosts show it to users who never knew your
codename. Any retired name or the repo's own codename appearing there is rejected.
The repo name is deliberately * not * derived. It is fixed when the repo is created and keeps
its development codename while the plugin gets a product name; `CODENAME` in `branding.conf`
records it so it can be kept out of user-facing text.
Checks for files the template doesn't ship (a `deploy.ps1` , say) report as `skip` and start
enforcing once you add them.
2026-08-29 10:24:52 +09:00
2026-08-21 13:08:18 +09:00
## Build
``` bash
# Install Rust via rustup: https://rustup.rs
# No VST3 SDK needed — NIH-plug bundles its own bindings.
2026-08-29 10:24:33 +09:00
# Build + bundle the CLAP (default)
2026-08-21 13:08:18 +09:00
cargo xtask bundle nih_plug_template --release
2026-08-29 10:24:33 +09:00
# Add the VST3 as well — see the licensing note below before you do this
cargo xtask bundle nih_plug_template --release --features vst3
2026-08-21 13:08:18 +09:00
```
2026-08-29 10:24:33 +09:00
**Output: ** `target/bundled/<name>.clap` (and `<name>.vst3` with `--features vst3` ), where
`<name>` is the `name` set in `bundler.toml` — **not ** the crate name. Out of the box that
means `target/bundled/Plugin Name.clap` .
2026-08-29 10:23:59 +09:00
On Linux the egui editor needs the GUI development headers:
``` bash
sudo apt install pkg-config libgl-dev libx11-dev libxcursor-dev libxcb1-dev \
libxcb-icccm4-dev libxcb-dri2-0-dev libx11-xcb-dev libxkbcommon-dev libxkbcommon-x11-dev
```
2026-08-21 13:08:18 +09:00
## Deploy
### FL Studio
2026-08-29 10:24:33 +09:00
Build with `--features vst3` first if you want the VST3 (see the licensing note below).
2026-08-21 13:08:18 +09:00
FL Studio scans `C:\Program Files\Common Files\VST3` by default, **ignores directory junctions **
(so a symlinked bundle is invisible to its scanner), and caches failed scans.
1. Copy the `.vst3` bundle to a folder FL scans (e.g. `C:\Program Files\Common Files\VST3` ).
2. Copy the `.clap` bundle to a folder FL scans (e.g. `C:\Program Files\Common Files\CLAP` ).
3. In FL Studio: **Options → Manage plugins → tick "Rescan previously failed plugins" → Find installed plugins ** , then search for your plugin name (set in `bundler.toml` ).
### Linux
``` bash
# CLAP
2026-08-29 10:23:59 +09:00
cp -r "target/bundled/Plugin Name.clap" ~/.clap/
2026-08-21 13:08:18 +09:00
2026-08-29 10:24:33 +09:00
# VST3 (only exists if you built with --features vst3)
cp -r "target/bundled/Plugin Name.vst3" ~/.vst3/
2026-08-21 13:08:18 +09:00
# Or globally:
2026-08-29 10:23:59 +09:00
# sudo cp -r "target/bundled/Plugin Name.clap" /usr/lib/clap/
2026-08-29 10:24:33 +09:00
# sudo cp -r "target/bundled/Plugin Name.vst3" /usr/lib/vst3/
2026-08-21 13:08:18 +09:00
```
## Project Structure
```
2026-08-29 10:24:33 +09:00
Cargo.toml # Package deps + the opt-in `vst3` feature — NIH-plug pinned to codename-206's rev
2026-08-21 13:08:18 +09:00
bundler.toml # Bundle name for `cargo xtask bundle`
2026-08-29 10:24:33 +09:00
src/lib.rs # Plugin skeleton — params, DSP, and egui editor, with one example gain param
2026-08-21 13:08:18 +09:00
xtask/ # Bundler xtask (cargo xtask bundle)
.cargo/config.toml # xtask alias
2026-08-31 23:11:47 +09:00
branding.conf # Canonical identity — the one place names are declared
check-branding.sh # Asserts every name is present where it belongs and no old one survives
brand-plugin.sh # Renames the plugin everywhere, then verifies with the checker
2026-08-29 10:24:52 +09:00
.gitattributes # Pins *.sh to LF so Windows checkouts keep a valid shebang
2026-08-31 22:27:35 +09:00
.github/workflows/ # CI: branding check, both feature sets build, test, bundle
2026-08-21 13:08:18 +09:00
```
2026-08-31 22:27:35 +09:00
## CI
`.github/workflows/ci.yaml` gates pushes and PRs on the branding check, a build of both
feature sets, `cargo test` , and a bundle — enough to catch a rename that only half-landed,
a `cfg(feature = "vst3")` gate that stopped compiling, or a `bundler.toml` that no longer
matches the crate name.
It targets `runs-on: rust-audio` , an **org-scoped ** runner label. A plugin cloned into a
different org will not see that runner and its jobs will queue indefinitely rather than fail
— register a runner advertising the label for that org, or change `runs-on` .
Deliberately not covered: plugin performance. Realtime cost can only be measured meaningfully
against a real audio deadline, and `Plugin::process()` cannot be called from a test at all
(`ProcessContext` requires a `Transport` , whose constructor and fields are `pub(crate)` to
nih-plug). Benchmark your DSP types directly if you need it.
2026-08-21 13:08:18 +09:00
## License
2026-08-31 22:00:33 +09:00
**GPL-3.0-or-later. ** See `LICENSE` for the terms and `NOTICE` for third-party attribution.
2026-08-21 13:08:18 +09:00
2026-08-31 22:00:33 +09:00
Plugins cloned from this template inherit GPL-3.0-or-later, because the template's own code
is under it. If you need a permissive plugin, this is the wrong starting point — write the
skeleton yourself against NIH-plug directly, which is ISC.
2026-08-21 13:08:18 +09:00
2026-08-31 22:00:33 +09:00
Parts of `src/lib.rs` derive from Robbert van der Helm's NIH-plug cookiecutter template and
`gain` example, both ISC. ISC is GPL-compatible, so that notice is retained in `NOTICE`
rather than superseded.
2026-08-29 10:24:33 +09:00
2026-08-31 22:00:33 +09:00
## VST3 and Steinberg
2026-08-29 10:24:33 +09:00
2026-08-31 22:00:33 +09:00
`vst3-sys` , which `nih_export_vst3!()` needs, is GPLv3-or-later — chosen by its own authors,
who describe their port as a derivative of Steinberg's SDK that "was not made in isolation."
That obligation is what makes a distributed VST3 build GPL-licensed, and it holds regardless
of any question about Steinberg's own rights. Being GPL-3.0-or-later already, this template
satisfies it.
2026-08-29 10:24:33 +09:00
2026-08-31 22:00:33 +09:00
No Steinberg SDK source is vendored or redistributed here, so there is nothing to accept at
build time. Separately from copyright, * * "VST" is a trademark of Steinberg Media Technologies
GmbH**; describing a distributed product as a VST plugin is governed by their trademark terms.
If you intend to distribute commercially under that name, that is the part worth legal advice.
CLAP carries none of this — no GPL dependency and no trademark encumbrance.