Build Your First Audio Plug-in with JUCE

ADC Japan - 2026-06-01

Anthony Nicholls, Attila Szarvas, Reuben Thomas, Tom Poole

 

 

https://audio.dev/adc-japan-26/schedule/

https://data.audio.dev/workshops/2026/build-first-plugin-with-juce/materials.zip

The JUCE Team

Anthony Nicholls

Attila Szarvas

Reuben Thomas

Tom Poole

Overview

  • Introduction to JUCE
  • Creating JUCE-based projects
  • Building audio plug-ins
  • Testing audio plug-ins

https://audio.dev/adc-japan-26/schedule/

https://data.audio.dev/workshops/2026/build-first-plugin-with-juce/materials.zip

Put the materials somewhere that is not managed by iCloud, OneDrive, DropBox, or similar.

 

Backup systems can interfere with the build process!

Ask us questions!

  • We will have breaks between the main sections
  • Please interrupt us
  • The slides are available in the workshop materials link in the online schedule

 

https://audio.dev/adc-japan-26/schedule/

https://data.audio.dev/workshops/2026/build-first-plugin-with-juce/materials.zip

macOS/iOS

Windows

Creating a plug-in using JUCE

Creating a plug-in using JUCE

  • Set up a JUCE project using the Projucer
  • Write the C++ plug-in code in an IDE
  • Compile the different plug-in formats
  • Add plug-in parameters
  • Create a GUI
  • Test the plug-in

Workshop materials

  • workspace is where we’ll edit the source code of our plug-ins
  • Numbered directories contain source code snapshots for each section
  • Plugins is where you will find your compiled plug-ins
  • TestHost is where you will find the TestHost application
  • Projucer is where you will find the Projucer application

#01: Creating a plug-in project

We’re now using the workshop checkpoints

The #01 in the title signals that the corresponding checkpoint for the beginning of the section is the 01 directory

To return to this point in the workshop you can delete the contents of the workspace folder and copy the contents of the 01 folder into the workspace folder

We’ve already done this for step #01, so we’re all starting from the same place

#01: Creating a plug-in project

Objectives of this section:

  • Go through the common project configurations settings in the Projucer
  • Export the project and open it in your IDE of choice (Xcode, Visual Studio, …)
  • Build an empty project
  • Load the plug-in in the TestHost

#01: Creating a plug-in project

Open the Projucer: Projucer/[your platform]/Projucer(.app/.exe)

 

In the Projucer, open the workspace/SimpleDelay.jucer project

  • File -> Open -> Select SimpleDelay.jucer in the workspace folder

#01: Project settings

#01: File browser

#01: Module settings

#01: Exporter Settings

#01: Generate IDE project files

#01: Generate IDE project files

We’ve seen a lot of Projucer settings but we don’t need to change any for this workshop!

Creating a Linux Makefile from the Projucer is a little different - there are no IDEs associated with Makefiles so after saving your project you need to run make from the command line

 

#01: CMake

If you are comfortable using CMake then there is a CMakeLists.txt in the root directory.

 

# on macOS, add [-G Xcode] to generate an Xcode project

 

 

#01: Project structure

#01: Running Standalone (macOS)

#01: Running Standalone (Windows)

#01: Running Standalone (Linux)

 

#01: Running the Standalone App

#01: Building plug-ins (macOS)

#01: Building plug-ins (Windows)

#01: Building plug-ins (Linux)

 

#01: Running in the TestHost

A simple host application for testing our plug-in can be found in the workshop materials:

  • TestHost/macOS/TestHost.app
  • TestHost/Win64/TestHost.exe
  • TestHost/Linux/TestHost

Launch the one appropriate for your platform.

#01: Running in the TestHost

  • Play different sounds
  • Plug-in section
  • Meters
  • Oscilloscope

#01: Running in the TestHost

Find the SimpleDelay plug-in that you’ve built in the Plugins folder and drop it onto the plug-in section of the TestHost app.

Click on the plug-in name to open the UI.

QUICK BREAK

#02: Modifying audio

Open your IDE project file:

  • workspace\Builds\VisualStudio2022\SimpleDelay.sln
  • workspace/Builds/macOS/SimpleDelay.xcodeproj

On Linux open the source file directly:

  • workspace/Source/PluginProcessor.cpp

#02: Edit the AudioProcessor

#02: Modifying audio

The SimpleDelayAudioProcessor class has a lot of methods

  • These have been automatically created by a Projucer template
  • Defines a lot of the behavior of the plug-in

The only one needed for this section is processBlock

#02: Modifying audio

#02: What does processBlock do?

processBlock is called repeatedly by the plug-in host with a chunk of audio to process

Be careful here! processBlock will be called on the audio thread, which is not the same as that used for drawing a GUI or handling mouse events (more on this a little later)

Don’t use headphones when developing

#02: juce::AudioBuffer<float>

AudioBuffer is a class containing non-interleaved channels of audio data represented as floats, and methods to access and modify them.

  • Non-interleaved : continuous lengths of audio data for each channel
  • Floats: each sample is represented by a floating point number in the range -1.0 to 1.0

processBlock is passed a reference to an AudioBuffer containing the incoming audio data. We create an audio effect by modifying this data.

For plug-ins the number of samples to process each block usually corresponds to the “buffer size” setting of the host (1024, 512, ...)

#02: Modifying audio

#02: Fixed gain reduction

#02: A functional plug-in!

  • Compile your projects
  • Open TestHost
  • This should reload the last-opened plug-in location
  • If this doesn’t work then find the plug-in created in the Plugins directory and drag it onto the plug-in section
  • Play some sounds
  • Toggle the bypass
  • Hear the gain reduction

#03: Plug-in parameters

Parameters are how plug-in hosts control plug-ins

They are exposed as part of the plug-in format’s interface

  • Hosts can query plug-ins to find out what parameters they offer

Parameters can be changed via:

  • The plug-in’s GUI
  • Host-provided plug-in views
  • Automation (although you can mark parameters as being non-automatable)
  • Preset switching

#03: Parameter types

Use classes derived from AudioProcessorParameter

  • Provide methods for getting and setting parameter values and properties
  • Added to, and then managed by, your AudioProcessor

JUCE provides some basic types to get you started

  • AudioParameterFloat
  • AudioParameterBool
  • AudioParameterChoice
  • AudioParameterInt

#03: Adding a parameter

#03: Adding a parameter

#03: Using a parameter in processBlock

#03: Use an automatically generated user interface

#03: Compile the plug-in and load it

#04: Creating the delay effect

Let’s add some less trivial audio processing

  • Implement a basic delay effect
  • Add some more parameters
  • Configure our audio processing algorithm in prepareToPlay

This section requires quite a lot of typing to complete. Start from the contents of the 05 folder and review the changes rather than typing along.

#04: What is a basic delay effect?

An echo of the incoming audio

The audio at time t is combined with a recursively attenuated audio at time (t - nD)

where D is the delay time and n = 1,2,3,4,...

Increasing the delay time D increases the time between echos

Increasing the attenuation decreases the time taken for the echos to fade away

Our SimpleDelay plug-in will feature a fixed delay time, but a variable feedback (opposite of attenuation) with a dry/wet mix control

#04: Add some more parameters

#04: The delay algorithm

A simple circular buffer of audio history

  • Audio samples are continuously written to the buffer in processBlock
  • When the capacity is exceeded we go back to the start
  • Existing audio data is attenuated, new audio data added on top
  • The size of the circular buffer will determine the (fixed) delay

#04: Add a circular buffer to PluginProcessor.h

#04: Configure the buffer in prepareToPlay

#04: Implement delay algorithm in processBlock

#05: Parameter management and state

Use an AudioProcessorValueTreeState to manage your parameters

  • Improved parameter handling
  • Serialise and deserialise your plug-ins state
  • (Later) Link parameters to GUI elements
This section requires quite a lot of typing to complete. Start from the contents of the 06 folder and review the changes rather than typing along.

#05: Adding an AudioProcessorValueTreeState

#05: Adding an AudioProcessorValueTreeState

#05: Parameter management

becomes

#05: Saving and restoring plug-in state

When plug-in hosts save and load projects, each plug-in must save and restore its state

  • getStateInformation (juce::MemoryBlock& destData)
  • setStateInformation (const void* data, int sizeInBytes)

The plug-in’s state must be serialised to, and deserialised from, a block of memory managed by the host.

#05: Saving plug-in state

#05: Restoring plug-in state

#05: Have a play!

BREAK

#06: Adding a GUI

Let’s display a custom GUI and draw some graphics

  • Basic shapes
  • Text

#06: Remove the generic GUI

#06: Our custom GUI

#06: Drawing in paint

#06: Threads

Be careful here!

  • The GUI is rendered on the “main” (GUI, message) thread
  • processBlock is called on the audio thread

It’s very easy to create race conditions, where one thread is modifying a bit of memory whilst another thread is reading it.

This is easily the most complicated aspect of working with real-time audio.

Using JUCE’s AudioParameter classes and an AudioProcessorValueTreeState makes things much simpler.

#06: Draw a square

#06: Lots of GUI code incoming

From now until the next break there will be a lot of code to add as we put together a GUI that’s more than a few basic shapes

Don’t worry about keeping up!

During and after the break there will be time to experiment with your own GUIs

#06: Preview: Something more advanced

#06: Something more advanced

#06: Something more advanced

#06: Preview: Something more advanced

#06: Something more advanced

#06: Something more advanced

#07: Components

Let’s create some interactive GUI elements

  • Add some sliders
  • Handle dynamic layout changes in resized

#07: JUCE Components

JUCE GUIs are trees of components

  • You can create your own; the AudioProcessorEditor is a Component
  • Parent components are responsible for laying out child components
  • Mouse events and keyboard focus can be passed between them
  • JUCE has a selection of common widget components you can use

#07: Preview: GUI with Sliders

#07: Workflow for adding Components

Adding Sliders, or any other Component requires the following basic steps

  1. Create a component member inside your editor class
    • Place the instance in the Editor class
    • Initialise it in the Editor constructor
  2. Attach it to the component tree by calling addAndMakeVisible()
  3. Specify the size and position in the Editor’s resized() → flexible layout

#07: Add some sliders to PluginEditor.h

#07: Configure the sliders in PluginEditor.cpp

#07: Layout the sliders in resized

#07: GUI with Sliders

#08: Connecting GUI controls to plug-in parameters

We would like to control the plug-in from the GUI

  • Use the AudioProcessorValueTreeState attachment classes to link Sliders to plug-in parameters

#08: Add some attachments to PluginEditor.h

#08: We need to pass the state to our editor

#08: Linking Slider to plug-in parameters

#09: The interactive plug-in

BREAK

Testing in Hosts

Debugging

What is a debugger?

  • The most useful tool in a programmer’s arsenal!
  • GDB, LLDB, Microsoft Visual Studio Debugger
  • CLI/GUI
  • Examine program state, pause when conditions are met

What is a breakpoint?

  • Can be set via the CLI or GUI
  • Program execution pauses when hit

Debugging

Debugging standalone plug-in is simple as we control the whole process

Debugging the plug-in inside an actual host is slightly more complicated as we are running inside a different process (the host)

Debuggers can attach to a separate process to allow you to debug and set breakpoints in your code

macOS

macOS

macOS

macOS

macOS

Windows

Windows

Windows

Windows

Linux

Linux

Out of process loading

  • Some hosts load plug-ins in a separate process
    • Logic Pro on Apple Silicon
    • AUv3 plugins (mostly)
    • Bitwig/Reaper depending on settings
  • Need to attach to the plug-in process not the host process:
    • Xcode: Debug->Attach to Process by PID or Name…
    • Visual Studio: Debug->Attach to Process…
    • GDB: attach <PID>

macOS Notarised Hosts

  • Since macOS Catalina (10.15), apps distributed outside the App Store must be notarised
  • Apps can only be debugged with if they have the com.apple.security.get-task-allow entitlement set to true
  • Must be false for notarisation to succeed
  • However it is possible to re-sign host binaries!

macOS Notarised Hosts

  • Get app’s entitlements using:
  • Modify them to include
  • Set the new entitlements using:

Testing with tools

pluginval

pluginval

auval

  • Apple’s command line AU verification tool
  • Tests basic AudioUnit functionality

Developing SimpleDelay further

General improvements:

  • Parameter change smoothing
  • A LookAndFeel to style the widgets
  • Accessibility
  • Presets
  • CMake

For this particular effect:

  • A variable delay length
  • Fractional delay lengths

Links