ADC
JAPAN
26

## 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

Welcome everyone

Please download the workshop materials

None of the JUCE team speaks any Japanese, but we will do our best to communicate. We have some bilingual helpers in the room, and if anyone in the audience is in a position to help us communicate please do so.

## The JUCE Team

Anthony Nicholls

Attila Szarvas

Reuben Thomas

Tom Poole

One of us will be speaking, which means that the rest of the team will be available to answer questions or fix any issues that come up during the workshop.

## 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

- 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

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: 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: 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

Preview for the changes we are about to make

## #06: Something more advanced

## #06: Preview: Something more advanced

Preview for the changes we are about to make

## #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

Sometimes your program will behave in unexpected and unwanted ways, and you’ll need to find out why so that you can stop that behaviour from happening.

Unwanted behaviour might include

Crashes, hangs

Poor performance

Incorrect output (glitches, reading uninitialised buffers)

Testing is about checking for and fixing unwanted behaviours.

Ideally, testing should happen during development, so that you find any problems before your users do!

## 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

First, let’s assume you’ve found a problem, but you’re not sure why it’s happening and you want to find the cause.

This is the main purpose of the debugger.

The debugger gives you additional information about the execution of another program. This additional information can help you to build up a picture of the problem in your mind, and work out why the program behaves in a particular way.

Different platforms have different debuggers:

Mac: lldb (Xcode)

Windows: Visual Studio debugger

Linux: lldb/gdb (command-line) - GUIs are available too (VS Code)

Debuggers let you

Pause the program, inspect the values of variables

Show the functions ‘below’ the current function on the stack

Useful to answer the questions “how/where is this function being called”

More advanced features

Evaluate functions and display their results while the program is running

Add logging output without having to rebuild the program, useful for tracing execution flow

The primary tool for achieving all of these functions is the breakpoint

_breaks_ (stops) the program at a specific _point_ - line number + file name, or function name, or after throwing an exception

While paused, you can inspect the state of the program.

## 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

Debugging a standalone program is easy. If you’ve been hitting ‘run’ in your IDE to launch the Standalone Plugin, you’ve already run your program under a debugger.

Debugging a plugin in a host is slightly more difficult. You need to tell the debugger to attach to the host program.

You can either launch the host under the debugger, or launch the host separately and attach the debugger later.

Attaching later can also be useful if your plugin gets into an odd state during normal use and you want to diagnose the problem.

## macOS

We’ll show the steps for all platforms before doing a live demo.

First, Xcode:

Go to Edit Scheme in the top bar of Xcode

## macOS

Select “Other…” from the Executable drop-down…

## macOS

Locate the test host

~/Development/workshop/materials

## macOS

Hit run, and the test host will launch under the debugger

## macOS

We can now click in the line-number column to add a breakpoint.

Here, we’ll add a breakpoint in the processBlock function.

If we now play some audio in the TestHost, the program should pause when execution reaches this line.

Try right-clicking on the breakpoint to see the additional options that are available

Logging

Conditional breaking

## Windows

Visual Studio is similar

Right-click on the plugin target that you want to test

Select “Properties”

## Windows

In the properties window, select “Debugging”, then “Command”, and navigate to the host that you want to test.

## Windows

Hit OK

## Windows

Now, the run button at the top of the window will launch the host with the debugger active.

We can add breakpoints by clicking in the column next to the line numbers.

## Linux

Linux is a bit different, as you’ll likely be using the debugger from the command line.

Go to the build directory

Make sure the plugin is built in the debugging configuration

Run gdb, passing the path to the host executable

Use ‘break’ to add a breakpoint on a specific line or function

Use ‘run’ to launch the program

Further details are probably out of scope for now

## Linux

This is how GDB will look when it hits the breakpoint we specified on the previous slide

## 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>

Sometimes it’s not easy to run the plugin host directly under a debugger.

Some hosts run plugins in a completely separate process to the main editor.

You’ll see this most often in Logic Pro on Apple Silicon, which runs all plugins out-of-process

E.g. Bitwig, Reaper (depending on settings), AUv3 plugins on all platforms, plugins in Logic on Apple Silicon

This is nice for users - if a plugin crashes, you won’t lose all of your work. You can just restart the audio engine.

We can’t launch the host process in the way we just showed, because the debugger would end up attached to the main editor, rather than the audio engine.

In this situation, we must launch the host normally, and then attach the debugger once the audio engine has started.

Show a quick demo in Xcode

Launch the TestHost, then attach to process in Xcode

There are also options to do something similar in Visual Studio, and with lldb/gdb

Debugging Logic requires some extra steps, including disabling System Integrity Protection. It’s often easier to debug in other hosts, but you can find instructions on the JUCE forum if necessary.

## 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!

Notarized hosts can pose issues when attempting to debug plugins on macOS

The notarization process is enforced for apps distributed outside the app store on macOS 10.15

This process automatically scans and validates programs for security issues

One of the prerequisites for notarization is that the app must not allow debugging

Accomplished by omitting com.apple.security.get-task-allow from the app’s entitlements

Entitlements are properties of an app, specified during signing, that tell the system what sorts of things it is allowed to do

Opening the camera/mic

Accessing location data

Starting a bluetooth connection

Debugging!

## macOS Notarised Hosts

- Get app’s entitlements using:

- Modify them to include

- Set the new entitlements using:

If we need to debug a specific notarized host that is causing problems, we can re-sign that host to allow debugging.

This process is destructive. MAKE A BACKUP of the host program!

Get the app entitlements

Modify the entitlements to allow debugging

Re-sign the app, passing the new set of entitlements

## Testing with tools

Debuggers are general purpose tools. You could use them to track down problems in most sorts of programs.

Tools also exist to stress-test plugins specifically.

These tools normally apply lots of different (unusual) configurations to your plugin, to ensure that it remains stable

You would use these sorts of tools to try to ensure that your plugin is stable under demanding conditions.

If the stress-test reveals any issues, you can then use a debugger to find out more details, and diagnose the problem.

## pluginval

- Cross-platform plug-in validation tool for testing AU/VST/VST3
- https://github.com/Tracktion/pluginval for source code and tagged releases
- Can be run on the command line or with a GUI

Pluginval is an open-source, cross-platform tool for stress-testing audio plugins

You’re able to read the code and step through the hosting code, which can make debugging easier

Command-line mode is useful for automatic testing, e.g. as part of an automated build pipeline (github actions, circle CI, azure pipelines), or for launching under a debugger

GUI mode is useful for batch-testing plug-ins

Pluginval includes a variety of tests

Enabling/disabling different bus layouts (mono, stereo, surround, sidechains) that your plugin claims to support

Testing different combinations of block size and sample rate

Saving plugin state, adjusting parameters, restoring state, and checking that all the parameter values have returned to the original state

Moving all parameters simultaneously and checking that the editor remains responsive

## pluginval

This is an example command-line invocation of pluginval

Strictness ranges from 0 to 10. You should attempt to pass tests at the highest strictness level.

Plugins may still be somewhat usable at lower strictness levels, but may break in more extreme/unusual scenarios.

## auval

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

Auval is similar to pluginval, but a component of macOS

Used by the system to test audio unit plugins

Plugins must pass validation to be usable in Logic/GarageBand

Use Terminal.app, rather than a third-party terminal. For unknown reasons, third-party terminals can’t always detect all installed plugins

The first command will list the identifiers and locations of the installed plugins. This can be useful if you can’t remember your plugin’s identifiers

The second command will run a set of tests on the specified plugin.

On Intel macs, you can easily attach a debugger to auval using the techniques demonstrated earlier.

On Arm machines, the auval binary doesn’t allow the debugger to be attached unless System Integrity Protection is disabled. This isn’t recommended, so exhaust other options first!

The newest version of Pluginval will automatically invoke auval when testing AudioUnit plugins

## 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

That’s all I have to say about testing.

Now I’d like to suggest some polish and improvements that you could make to the simple delay plugin

You might notice that when a parameter is moved rapidly while audio is playing, you hear quiet pops and crackles, often referred to as “zipper noise”

This happens because each block of audio is asked to play with a completely different parameter value. Imagine an automated gain parameter - when the gain changes suddenly between blocks of audio, there’ll be a sudden change in the gain at the start of each audio block, which sounds like a click

To counteract these glitches, you can smooth the gain level from its value at the end of the last block to its new position.

You might find JUCE’s LinearSmoothedValue class helpful to implement this smoothing.

Custom look-and-feel

At the moment, our sliders are using the standard JUCE styling, but most plugins have recognisable styling that often ties into the plugin’s brand. You can change the styling of JUCE’s widgets by writing a custom LookAndFeel class.

Consider your users with accessibility needs

Our current interface doesn’t even have labels for the parameter sliders!

It’s a lot easier to plan for this from the outset, rather than attempting to retrofit

Test with a screen reader, Accessibility Inspector on macOS, Accessibility Insights on Windows

Built-in presets

The AudioProcessor class includes functions that can be overridden to get/set preset data, so that users can quickly audition your plugin, and find a suitable foundation for their own parameter tweaks.

Adjustable delay time

At the moment, the delay time is constant, but you might want a tempo-synced effect. In order to implement this, you’d need to add a larger delay buffer, and allow adjusting the ‘read’ position. For tempo-sync, look at JUCE’s AudioPlayHead class.

Much like gain smoothing, you’d probably want to smooth the position of the read head, so that it doesn’t produce discontinuities when it is moved. For truly ‘smooth’-sounding output, the playhead might need to fall between sample positions. Writing such a sub-sample interpolation is a bit tricky, but luckily JUCE includes a DelayLine class in the DSP module, which implements this feature. The DSPModulePluginDemo in the JUCE repository shows one way of using the delay line in a plug-in project.

Moving to CMake

Once you’ve built a few plug-ins, you might find it easier to maintain them if you can build them all in one go, especially if they share code.

Although the Projucer makes it easy to get started, CMake may be a better choice for larger projects with multiple targets.

There are heavily-annotated examples of CMake-based projects in the JUCE repository, under `examples/CMake`.

Installers and distribution

## Links

GitHub: https://github.com/juce-framework/JUCE

Forum: https://forum.juce.com

Course: https://juce.com/learn/course

Tutorials: https://juce.com/learn/tutorials

Here are some resources that you might find useful as you continue your audio development journey

JUCE’s source is on Github. It’s a good idea to update frequently, as we’re always fixing bugs and adding features.

The Forum is a great place to ask questions that you’re unable to answer by reading the code and documentation. Lots of experienced JUCE users are active answering questions.

There's a free JUCE course available online, with a full curriculum covering much more than we have done in this workshop. It includes video lectures, quizzes, and exercises to help you learn the material.

We also have a wide range of tutorials with example projects that you can work through.

The JUCE team is available now and throughout the conference to answer your questions.