This is a JUCE plugin starter template using CMake and environment-based configuration for folks new to audio plugin development on macOS. It allows you to create standalone apps and audio plugins (AU/VST3) for macOS using Xcode. Itβs designed for quick setup, ease of customization, and modern JUCE development workflows.
This is the fastest way to test-drive the JUCE Plugin Starter. It assumes you have Xcode installed and will:
- β Install all required dependencies (doesn't assume you have git)
- β Clone this repo and set up your environment
- β Run a guided script to create your new plugin repo and push it to GitHub
- β Download JUCE and generate an Xcode project
Heads up: This command runs several scripts and installs multiple components. To avoid surprises, itβs a good idea to read through the full README before running it in your terminal. And, to watch the output in the event you're asked to take manual actions.
Setup Instructions: Paste the entire section below directly into your terminal.
# Install required tools (Xcode CLT, Homebrew, CMake, PluginVal, etc.)
bash <(curl -fsSL https://raw.githubusercontent.com/danielraffel/JUCE-Plugin-Starter/main/dependencies.sh)
# The commands above install software like Homebrew.
# During installation, you may be prompted to add Homebrew to your PATH manually:
# echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.bash_profile
# eval "$(/opt/homebrew/bin/brew shellenv)"
# Clone the starter project and set up environment
git clone https://github.com/danielraffel/JUCE-Plugin-Starter.git
cd JUCE-Plugin-Starter
cp .env.example .env
# Run the first-time setup to configure your plugin project
chmod +x ./init_plugin_project.sh
./init_plugin_project.sh
# Generate and open the Xcode project (downloads JUCE on first run)
chmod +x ./generate_and_open_xcode.sh
./generate_and_open_xcode.sh
-
π¨βπ» You can now open your project folder in your favorite IDE (Xcode, VS Code, Cursor, Windsurf, etc) and start building your JUCE plugin.
-
β Once setup is complete, run this command whenever you need to manually rebuild your project and reopen it in Xcode during development. If youβre collaborating with an AI developer tool, here are some common ways to automate executing this script.
./generate_and_open_xcode.sh .
β Note: This setup gives you a fully working plugin on your local machine for development and testing.
π§³ To share or distribute your plugin to others, you'll need to configure code signing and notarization β when ready see π¦ How to Distribute Your Plugin for full instructions.
π Skip to Full Quick Start β
- βΉοΈ Overview
- π§° Prerequisites
- π Quick Start
- π§± Build Targets
- π Where Files Are Generated (Plugins + App)
- π Auto-Versioning Plugin Builds in Logic Pro
- π Customize Your Plugin
- π οΈ How to Edit
CMakeLists.txt
- π¦ Project File Structure
- π‘ Tips
- π¦ How to Distribute Your Plugin
- π Resources
To build and develop plugins with this template, youβll need:
- macOS 15.0 or later
- Xcode (latest version)
- Recommended: Additional IDE with Support for 3rd Party AI Models (Alex Sidebar, Cursor, Windsurf, Trae, or VSCode)
Before building the project, you need to install several development tools.
You can choose one of the following setup methods:
- Automated Dependency Setup β Recommended for most users.
- Manual Dependency Setup β For those who prefer full control.
Use the included dependencies.sh
script. It checks for each required tool and installs it automatically if missing. This is typically needed only for a first-time setup.
bash <(curl -fsSL https://raw.githubusercontent.com/danielraffel/JUCE-Plugin-Starter/main/dependencies.sh)
The script handles:
- β Xcode Command Line Tools
- β Homebrew
- β CMake
- β PluginVal
It also includes optional installs, commented out by default:
- Faust β DSP prototyping compiler
- GoogleTest β C++ unit testing
- Python 3, pip3, and behave β for behavior-driven development (BDD)
βοΈ When you run
dependencies.sh
software like Homebrew may ask you to do additonal configurations to complete your setup:
# During installation, you may be prompted to add Homebrew to your PATH manually:
# echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.bash_profile
# eval "$(/opt/homebrew/bin/brew shellenv)"
βοΈ To enable optional tools, simply uncomment the relevant lines in the script.
If you prefer, you can install all required tools manually:
Tool | Purpose | Manual Install Command |
---|---|---|
Xcode Command Line Tools | Xcode compiler & tools | xcode-select --install |
CMake | Build system configuration | brew install cmake |
Homebrew | macOS package manager | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" |
PluginVal | Plugin validation & testing | brew install --cask pluginval |
Faust (optional) | DSP prototyping compiler | brew install faust |
GoogleTest or Catch2 (optional) | C++ unit testing | brew install googletest or brew install catch2 |
Python 3 + behave (optional) | Natural language test automation | brew install python && pip3 install behave |
JUCE | Audio plugin framework (AU/VST3) | Automatically downloaded by CMake/FetchContent |
π‘ This setup will βjust workβ if you clone this repo into your home folder (i.e., run cd in Terminal before cloning).
git clone https://github.com/danielraffel/JUCE-Plugin-Starter.git
cd JUCE-Plugin-Starter
cp .env.example .env
Update your .env
file to reflect:
PROJECT_NAME=MyCoolPlugin
PROJECT_BUNDLE_ID=com.myname.mycoolplugin
PROJECT_PATH=~/JUCE-Plugin-Starter
COMPANY_NAME="Your Company Name"
JUCE_REPO=https://github.com/juce-framework/JUCE.git
JUCE_TAG=8.0.7
GITHUB_USERNAME=danielraffel
BASE_PROJECT_VERSION="1.0."
# --- Apple Notarization ---
# You only need to edit the fields below when you're ready to distribute your plugin.
# Until then, feel free to leave these default placeholder values as-is.
APPLE_ID=your@email.com
APP_SPECIFIC_PASSWORD=your-app-password
APP_CERT="Developer ID Application: Your Name (TEAM_ID)"
INSTALLER_CERT="Developer ID Installer: Your Name (TEAM_ID)"
TEAM_ID=YOUR_TEAM_ID
After cloning and configuring .env
, remember:
PROJECT_NAME
is used as the name of your plugin. This will appear in DAWs like Logic, so name it accordingly.COMPANY_NAME
sets the developer name displayed in the plugin browser.
π‘ On macOS, .env files are hidden by default in Finder. Press
Cmd + Shift + .
to show or hide hidden files. You can also edit the file in Terminal using:
nano .env
If you're planning to use this template to build your own plugin and eventually publish it to GitHub, this script is designed to help you do that quickly and cleanly. The script is smart and will:
- π Auto-detect if you're working in a JUCE project directory that doesn't match your .env settings
- π·οΈ Automatically suggest renaming your project folder to match your plugin name
- π Create basic plugin source files if they don't exist (ready-to-build template)
- π‘οΈ Confirm everything before making changes so you stay in control
Just run the interactive setup script (it will help you configure everything):
chmod +x ./init_plugin_project.sh
./init_plugin_project.sh
What the script does:
- π Smart path detection - notices if you're in a different directory than your .env expects and offers to fix it
- π§ Load and validate your .env settings
- βοΈ Interactive editing of project name, GitHub username, and project path
- π Intelligent folder renaming - suggests renaming to match your plugin name (e.g., JUCE-Plugin-Starter β DelayR)
- π§ Script setup - makes build scripts executable (post_build.sh, generate_and_open_xcode.sh)
- π Template source file creation - generates PluginProcessor.cpp/.h and PluginEditor.cpp/.h if missing
- π Repository visibility choice - asks if you want public or private
- β Clear confirmation - shows exactly what will be created before proceeding
- π§Ή Clean slate - removes the template's Git history
- π GitHub integration - creates your new repository using the GitHub CLI (gh)
- π First commit - pushes your initial code and provides next steps
Example flow:
- π PATH MISMATCH DETECTED - Updates .env to match your current location
- π Edit PROJECT_NAME? β "DelayR"
- π Rename folder to match project name? β JUCE-Plugin-Starter becomes DelayR
- π§ Setting up build scripts... β Makes scripts executable for immediate use
- π Checking for basic plugin source files... β Creates template files if missing
- π Make this a private repository? β Choose public or private
- β Proceed with project creation? β Final confirmation
- π Success! Ready to start coding your plugin
π‘ Highly recommended for first-time users β the script handles all the setup automatically (including creating working plugin template files) while keeping you informed every step of the way. After running this script, you'll have a complete, buildable JUCE plugin ready to customize!
π‘ First time setup: When you first run
./generate_and_open_xcode.sh
, CMake will automatically download JUCE tobuild/_deps/juce-src/
inside your project directory. This may take a few minutes on the first run. JUCE will be reused for subsequent builds and cleaned up when you delete thebuild/
directory.
Before running the script for the first time:
chmod +x ./generate_and_open_xcode.sh
Then generate your project:
./generate_and_open_xcode.sh
β
No need to run cmake
manually β it's handled for you.
Once the project is open in Xcode, you can build:
- β Standalone App
- β AudioUnit Plugin (AU) β for Logic Pro, GarageBand
- β VST3 Plugin β for Reaper, Ableton Live, etc.
Switch targets using the Xcode scheme selector.

Make sure the
FORMATS AU VST3 Standalone
line is present inCMakeLists.txt
.
When you build your plugin from Xcode, the following file types are generated and installed in the standard macOS plugin locations:
- Audio Unit (AU) Component:
~/Library/Audio/Plug-Ins/Components/YourPlugin.component
- VST3 Plugin:
~/Library/Audio/Plug-Ins/VST3/YourPlugin.vst3
- Standalone App:
Found inside your build folder in your `PROJECT_NAME_artefacts` debug or release folder.
These paths are standard for macOS plugin development and are used by DAWs like Logic Pro, Ableton Live, Reaper, etc.
This template includes a post-build script (scripts/post_build.sh
) that automatically versions your plugin bundle after each build, ensuring Logic Pro correctly reloads the updated component.
How Versioning Works:
-
The base version is set in your
.env
file withBASE_PROJECT_VERSION
. By default, it's:BASE_PROJECT_VERSION="1.0."
You can edit this value to manage your major/minor version. The script will always append a build timestamp to ensure each build is unique.
-
After each build, the script:
- Reads
BASE_PROJECT_VERSION
from.env
(or uses a default if not found). - Appends a timestamp to generate a unique version (e.g.,
1.0.2505302321
). - Updates your pluginβs
Info.plist
with:CFBundleShortVersionString
= base version (e.g.,1.0.
)CFBundleVersion
= base version + timestamp (e.g.,1.0.2505302321
)
- Only the full (timestamped) version is visible in the
Info.plist
, not in the plugin UI.
- Reads
-
The script is triggered from your CMake build with:
add_custom_command(TARGET ${PROJECT_NAME}_AU POST_BUILD COMMAND "${CMAKE_SOURCE_DIR}/scripts/post_build.sh" "$<TARGET_BUNDLE_DIR:${PROJECT_NAME}_AU>" COMMENT "Updating Info.plist version for ${PROJECT_NAME}_AU" VERBATIM )
What This Solves:
- Ensures Logic Pro re-recognizes your Audio Unit after every build
- Prevents stale cache/version issues
- Keeps development iterative and frustration-free
How to Manage Versions:
- Edit
.env
and changeBASE_PROJECT_VERSION
(e.g., bump from1.0.
to1.1.
for a new feature). - The timestamp is always unique per build, so you don't have to manage patch versions manually.
- The timestamped build version is only visible in the plugin's
Info.plist
, not in the UI.
Customization:
- You can modify
scripts/post_build.sh
to change how versions are generated or update which fields are set inInfo.plist
.
Edit the files in Source/
:
PluginProcessor.cpp / .h
β DSP and audio enginePluginEditor.cpp / .h
β UI layout and interaction
Add more .cpp/.h
files as needed for a modular architecture.
Your CMakeLists.txt
is where the pluginβs structure and build config live. Open it with any code editor.
All CMake configuration is centralized in the top-level CMakeLists.txt
file. If you need to:
- Add source files
- Link dependencies
- Define feature flags
- Toggle plugin modes
- Set test builds
Then this is where to do it.
π Tip: If you only modified non-CMake files and want a quicker build, you can skip regeneration using:
SKIP_CMAKE_REGEN=1 ./generate_and_open_xcode.sh
Otherwise, always use:
./generate_and_open_xcode.sh
If you're developing using an agent like Claude Code it will determine which build approach is best using instructions in the CLAUDE.md
in this repository.
target_sources(${PROJECT_NAME} PRIVATE
Source/PluginProcessor.cpp
Source/PluginEditor.cpp
Source/MyFilter.cpp
Source/MyFilter.h
)
target_link_libraries(${PROJECT_NAME} PRIVATE
juce::juce_audio_utils
juce::juce_graphics
juce::juce_osc # <-- newly added module
)
FORMATS AU VST3 Standalone
To skip AU, just remove it:
FORMATS VST3 Standalone
target_compile_definitions(${PROJECT_NAME} PRIVATE
USE_MY_DSP_ENGINE=1
)
To ensure your project builds against a specific minimum macOS version, set the CMAKE_OSX_DEPLOYMENT_TARGET before defining the project in your CMakeLists.txt.
# Set minimum macOS version before defining the project
set(CMAKE_OSX_DEPLOYMENT_TARGET "15.0" CACHE STRING "Minimum macOS version")
After making changes, just re-run:
./generate_and_open_xcode.sh
JUCE-Plugin-Starter/
βββ .env.example β Template for your environment variables
βββ CLAUDE.md β Project details for Claude Code
βββ CMakeLists.txt β Main build config for your JUCE project
βββ init_plugin_project.sh β Script that will reinitialize this repo to make it yours, configure, rename and push it to GH
βββ README.md β You're reading it
βββ generate_and_open_xcode.sh β Script that loads `.env`, runs CMake, and opens Xcode
βββ scripts/ β Automation / helper scripts
β βββ post_build.sh β Auto-increments bundle version so Logic reloads builds
β βββ sign_and_package_plugin.sh β Auto-package plug-in(s) for safe, notarized distribution via PKG and DMG files
βββ Source/ β Your plugin source code
β βββ PluginProcessor.cpp/.h
β βββ PluginEditor.cpp/.h
βββ build/ β Generated by CMake (can be deleted anytime)
βββ YourPlugin.xcodeproj β Generated Xcode project
~/.juce_cache/ β Shared JUCE location (outside project)
βββ juce-src/ β JUCE framework (shared across all projects)
- β Shared across projects: Multiple JUCE projects use the same download
- β Survives build cleaning: rm -rf build won't delete JUCE
- β Version controlled: Different projects can use different JUCE versions via JUCE_TAG
π‘ You can safely
rm -rf
build without re-downloading JUCE every time.
The generate_and_open_xcode.sh
script includes a line that automatically opens the generated Xcode project. Since Cursor doesnβt require this, I strongly recommend commenting out the following section:
# Open the generated Xcode project
if [ -d "$XCODE_PROJECT" ]; then
open "$XCODE_PROJECT"
else
echo "Xcode project not found: $XCODE_PROJECT"
exit 1
fi
To use generate_and_open_xcode.sh
with AlexCodes.app, Iβve found the following project prompt helpful for managing when and how the Xcode project is compiled and opened:
Whenever the Xcode project file needs to be regenerated use run_shell to execute $PROJECT_PATH/generate_and_open_xcode.sh

To use generate_and_open_xcode.sh
with Claude Code, Iβve created a CLAUDE.md and added it to the root of the repo. It instructs the agent how and when to properly recreate the project file. It can help you build and run the project by invoking the proper script:
./generate_and_open_xcode.sh
Claude will determine whether a full regeneration is needed based on recent file changes or build errors.. The CLAUDE.md explains when it's best to regenerate the Xcode project and how to optionally skip regeneration for faster builds using:
SKIP_CMAKE_REGEN=1 ./generate_and_open_xcode.sh
Once your plugin is built and tested, you can package it for safe, notarized distribution via Appleβs system using the built-in sign_and_package_plugin.sh
script.
π Interested in more details? Check out this walkthrough on macOS audio plug-in code signing and notarization.
- Join at developer.apple.com/programs
Youβll need two certificates installed in Keychain Access (login keychain):
- Developer ID Application
- Developer ID Installer
These allow Apple to verify your identity and authorize your software for distribution.
- Go to Apple Developer Certificates Portal
- Click β βCreate Certificateβ
- Choose:
Developer ID Application
Developer ID Installer
- Follow Appleβs steps to:
- Create a Certificate Signing Request (CSR) using Keychain Access
- Download the signed certificate
- Double-click it to install into your Keychain
In Terminal, run:
security find-identity -v -p codesigning
You should see both listed, like:
1) A1B2C3D4E5... "Developer ID Application: Your Name (TEAMID)"
2) F6G7H8I9J0... "Developer ID Installer: Your Name (TEAMID)"
Make note of the exact names β you'll reference these in your .env
file.
- Go to appleid.apple.com
- Under Sign-In and Security, click App-Specific Passwords
- Click β "Generate App-Specific Password" and consider using your project name as the identifier
- Make sure to store this password safely β itβs required for every notarization and must be added to your .env
Add these values to your main .env
file:
# Notarization credentials
APPLE_ID=your@email.com
APP_SPECIFIC_PASSWORD=abcd-efgh-ijkl-mnop
APP_CERT="Developer ID Application: Your Name (TEAM_ID)"
INSTALLER_CERT="Developer ID Installer: Your Name (TEAM_ID)"
TEAM_ID=YOUR_TEAM_ID
π‘ Always make sure your
.env
file is listed in.gitignore
to avoid exposing credentials.
sign_and_package_plugin.sh
will automatically detect and package the following plugin formats if they exist:
Format | Extension | Path |
---|---|---|
AU | .component |
~/Library/Audio/Plug-Ins/Components/ |
VST3 | .vst3 |
~/Library/Audio/Plug-Ins/VST3/ |
AAX | .aaxplugin |
/Library/Application Support/Avid/Audio/Plug-Ins/ |
- The script signs, notarizes, and staples each format (if found)
- All formats are bundled into a single
.pkg
installer - The
.pkg
is signed and notarized - Finally, the
.pkg
is included in a ready-to-share.dmg
π‘ Missing formats are skipped, but the script will print a warning if none are found and exit gracefully.
From your project root:
cd scripts
chmod +x sign_and_package_plugin.sh
./sign_and_package_plugin.sh
This script will:
- β
Sign and notarize your
.component
- β
Create a signed
.pkg
installer and notarize it - β
Bundle it into a distributable
.dmg
π Output files (
.zip
,.pkg
,.dmg
) will be saved to your Desktop for easy sharing.