Skip to content
Development
Skill

/juce-guide

Step-by-step guide for building a JUCE audio plugin from scratch. Use when the user asks "how do I create a JUCE plugin", "set up a JUCE project", "add parameters to my JUCE plugin", "package my VST3", or "how do I wire up APVTS". Trigger on phrases like "create a JUCE plugin",

From plugin
sonic-skills
2218 skills

Claude Code

$ npx -y skills add kunitoki/sonic-skills --skill juce-guide --agent claude-code

SKILL.md

juce-guide.SKILL.md
name: juce-guide
description: >
  Step-by-step guide for building a JUCE audio plugin from scratch. Use when the user
  asks "how do I create a JUCE plugin", "set up a JUCE project", "add parameters to my
  JUCE plugin", "package my VST3", or "how do I wire up APVTS". Trigger on phrases like
  "create a JUCE plugin", "JUCE CMake setup", "AudioProcessor skeleton", "add APVTS
  parameters", or "codesign my plugin on macOS".

JUCE Audio Plugin — Build Guide

Five steps from empty folder to distributable plugin. Do each step in order; skip none.

Step 1 — Project setup (CMake + JUCE)

  • Add JUCE as a git submodule or FetchContent target; call `add_subdirectory(JUCE)`.
  • Use `juce_add_plugin(MyPlugin FORMATS VST3 AU Standalone ...)` to declare the plugin target.
  • Set `PLUGIN_MANUFACTURER_CODE`, `PLUGIN_CODE`, `IS_SYNTH`, `NEEDS_MIDI_INPUT` in the same call.
  • Link `juce::juce_audio_processors juce::juce_audio_utils juce::juce_dsp` to the target.

Step 2 — AudioProcessor skeleton

  • Subclass `juce::AudioProcessor`; override `prepareToPlay`, `releaseResources`, `processBlock`.
  • In `prepareToPlay`: store `sampleRate` and `maximumBlockSize`, allocate internal buffers, reset all state (filters, envelopes, delay lines — unconditionally).
  • In `processBlock`: read only pre-cached raw parameter pointers; never allocate, never call JUCE String ops or DBG().
  • Implement `getStateInformation` / `setStateInformation` using `apvts.copyState()` and `apvts.replaceState()` with a `MemoryOutputStream` / `MemoryBlock`.

Step 3 — Parameters via APVTS

  • Declare a static `createParameterLayout()` method that returns `AudioProcessorValueTreeState::ParameterLayout`.
  • Construct `apvts(*this, nullptr, "Parameters", createParameterLayout())` as a member.
  • After constructing APVTS, cache raw pointers once: `gainParam = apvts.getRawParameterValue("Gain")`.
  • In `processBlock`, dereference with `gainParam->load()` — no APVTS calls on the audio thread.
  • Use `AudioProcessorValueTreeState::ParameterAttachment` (or `SliderAttachment`) in the editor, never in the processor.

Step 4 — Editor and UI

  • Subclass `juce::AudioProcessorEditor`; call `setSize(w, h)` in the constructor.
  • Override `paint` for background / labels; override `resized` to lay out components with `setBounds`.
  • Declare `juce::Slider` members; attach each via `juce::AudioProcessorValueTreeState::SliderAttachment`.
  • Keep all UI state in the editor — the processor must function correctly with no editor open.
  • The editor holds a reference to the processor (passed in constructor); never store a raw editor pointer in the processor.

Step 5 — Build, test, package

  • Configure: `cmake -B build -DCMAKE_BUILD_TYPE=Release`
  • Build: `cmake --build build --config Release`
  • Locate artefacts under `build/MyPlugin_artefacts/Release/`.
  • macOS codesign: `codesign --deep --force --sign "Developer ID Application: ..." MyPlugin.vst3` then `spctl --assess --verbose MyPlugin.vst3`.
  • Notarize for distribution: `xcrun notarytool submit MyPlugin.pkg --apple-id ... --wait`.
  • Validate in a DAW host or use the JUCE `AudioPluginHost` utility (`extras/AudioPluginHost`).

Common pitfalls

| Pitfall | Correct approach | |---------|-----------------| | Calling `apvts.getParameter()` in `processBlock` | Cache `getRawParameterValue()` once after APVTS construction | | Allocating in `processBlock` | Allocate in `prepareToPlay` only | | Not resetting state on `prepareToPlay` re-entry | Always reset unconditionally | | Storing editor pointer in processor | Use `createEditor()` factory; processor owns no editor reference | | Missing `JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR` | Add to every `AudioProcessor` and `AudioProcessorEditor` subclass |

Use the `juce-review` skill to audit finished processor code for thread-safety violations.

Read more
@kunitoki
Ships withsonic-skills

Precision audio-engineering skills for AI agents. Sonic Skills is a curated pack of Markdown skills for reviewing, debugging, explaining, and implementing audio software.

Stars on GitHub

Momentum

stars in total
22
Star history of kunitoki/sonic-skills on GitHub, a chart by star-history.com

Other skills on sonic-skills.

au-review

au-review

Reviews AudioUnit v2/v3 plugin implementations for spec compliance, thread safety, and correctness. Use when the user asks to…

audio-dsp-review

audio-dsp-review

Reviews audio DSP and audio processing code for realtime safety violations. Use whenever the user asks to review, audit, or check…

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills11d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc13d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react2d252k
@facebook@facebookInstall Skill

Questions about juce-guide.

Which coding agents can use juce-guide?
juce-guide installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.