Skip to main content
Back to Blog
VSTAudio PluginsC++JUCEMusic ProductionTutorial

VST Plugin Development for Beginners: Your First Audio Plugin

A beginner-friendly guide to building your first VST audio plugin. Learn the fundamentals of digital audio, plugin architecture, and how to create a simple gain plugin from scratch.

6 min read

Building VST plugins is one of the most rewarding areas of software development. You create tools that musicians use to make art. This guide will take you from zero to your first working plugin.

As someone who transitioned from Berklee to software engineering, I've found audio plugin development to be the perfect blend of my two passions. Once you're ready for more advanced topics, check out my complete JUCE guide.

What is a VST Plugin?

VST (Virtual Studio Technology) is a standard for audio plugins developed by Steinberg. VST plugins process or generate audio within a DAW (Digital Audio Workstation) like Ableton Live, FL Studio, or Logic Pro.

There are three types:

  • VST Instruments (VSTi) - Generate sound (synths, samplers)
  • VST Effects - Process audio (EQ, compression, reverb)
  • VST MIDI - Process MIDI data

Prerequisites

Before diving in, you'll need:

  1. C++ basics - Variables, functions, classes, pointers
  2. A DAW - For testing (Reaper is free to try)
  3. JUCE Framework - Download from juce.com
  4. IDE - Xcode (Mac), Visual Studio (Windows)

Don't worry if your C++ is rusty - audio programming is a great way to learn.

Understanding Digital Audio

Audio in computers is represented as samples - numbers that represent the amplitude of a sound wave at a moment in time.

Sample Rate: How many samples per second
- 44,100 Hz (CD quality)
- 48,000 Hz (Video standard)
- 96,000 Hz (High resolution)

Bit Depth: Precision of each sample
- 16-bit: 65,536 possible values
- 24-bit: 16.7 million values
- 32-bit float: Used internally for processing

When you process audio, you're literally doing math on these numbers.

Plugin Architecture

Every audio plugin has two parts:

1. The Processor

Where audio gets processed. This code runs on the audio thread - a high-priority thread that must never block or allocate memory.

void processBlock(AudioBuffer<float>& buffer, MidiBuffer& midi)
{
    // This runs thousands of times per second
    // Process audio here
}

2. The Editor

The visual interface users see. This runs on the GUI thread - separate from audio processing.

void paint(Graphics& g)
{
    // Draw your interface here
}

Critical rule: The audio thread and GUI thread must communicate carefully to avoid glitches.

Your First Plugin: Simple Gain

Let's build a gain plugin - it makes audio louder or quieter.

Step 1: Create the Project

  1. Open JUCE's Projucer
  2. Create new project → Audio Plugin
  3. Name it "SimpleGain"
  4. Enable VST3 and AU (Mac) formats
  5. Open in your IDE

Step 2: Add a Parameter

In PluginProcessor.h:

class SimpleGainProcessor : public juce::AudioProcessor
{
public:
    // ... constructor and methods ...

    juce::AudioProcessorValueTreeState parameters;

private:
    std::atomic<float>* gainParameter = nullptr;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SimpleGainProcessor)
};

In PluginProcessor.cpp constructor:

SimpleGainProcessor::SimpleGainProcessor()
    : AudioProcessor(BusesProperties()
          .withInput("Input", juce::AudioChannelSet::stereo(), true)
          .withOutput("Output", juce::AudioChannelSet::stereo(), true)),
      parameters(*this, nullptr, "Parameters",
          {
              std::make_unique<juce::AudioParameterFloat>(
                  "gain",           // Parameter ID
                  "Gain",           // Parameter name
                  0.0f,             // Minimum value
                  2.0f,             // Maximum value
                  1.0f              // Default value
              )
          })
{
    gainParameter = parameters.getRawParameterValue("gain");
}

Step 3: Process Audio

In processBlock:

void SimpleGainProcessor::processBlock(
    juce::AudioBuffer<float>& buffer,
    juce::MidiBuffer& midiMessages)
{
    // Get the current gain value (thread-safe)
    const float gain = gainParameter->load();

    // Process each channel
    for (int channel = 0; channel < buffer.getNumChannels(); ++channel)
    {
        // Get pointer to channel's samples
        float* channelData = buffer.getWritePointer(channel);

        // Apply gain to each sample
        for (int sample = 0; sample < buffer.getNumSamples(); ++sample)
        {
            channelData[sample] *= gain;
        }
    }
}

Step 4: Create the UI

In PluginEditor.h:

class SimpleGainEditor : public juce::AudioProcessorEditor
{
public:
    SimpleGainEditor(SimpleGainProcessor&);
    ~SimpleGainEditor() override;

    void paint(juce::Graphics&) override;
    void resized() override;

private:
    SimpleGainProcessor& processor;

    juce::Slider gainSlider;
    juce::Label gainLabel;

    std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment>
        gainAttachment;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SimpleGainEditor)
};

In PluginEditor.cpp:

SimpleGainEditor::SimpleGainEditor(SimpleGainProcessor& p)
    : AudioProcessorEditor(&p), processor(p)
{
    // Configure the slider
    gainSlider.setSliderStyle(juce::Slider::RotaryHorizontalVerticalDrag);
    gainSlider.setTextBoxStyle(juce::Slider::TextBoxBelow, false, 80, 20);
    addAndMakeVisible(gainSlider);

    // Configure the label
    gainLabel.setText("Gain", juce::dontSendNotification);
    gainLabel.setJustificationType(juce::Justification::centred);
    addAndMakeVisible(gainLabel);

    // Connect slider to parameter
    gainAttachment = std::make_unique<
        juce::AudioProcessorValueTreeState::SliderAttachment>(
            processor.parameters, "gain", gainSlider);

    // Set plugin window size
    setSize(200, 250);
}

void SimpleGainEditor::paint(juce::Graphics& g)
{
    g.fillAll(juce::Colours::darkgrey);
}

void SimpleGainEditor::resized()
{
    auto bounds = getLocalBounds().reduced(20);
    gainLabel.setBounds(bounds.removeFromTop(30));
    gainSlider.setBounds(bounds);
}

Step 5: Build and Test

  1. Build the project in your IDE
  2. Copy the VST3 file to your plugins folder
  3. Open your DAW and scan for new plugins
  4. Load SimpleGain on a track
  5. Play audio and turn the knob!

Common Beginner Mistakes

1. Allocating Memory in processBlock

// BAD - allocates memory on audio thread
void processBlock(AudioBuffer<float>& buffer, MidiBuffer&)
{
    std::vector<float> temp(buffer.getNumSamples()); // DON'T DO THIS
}

// GOOD - pre-allocate in prepareToPlay
void prepareToPlay(double sampleRate, int samplesPerBlock)
{
    tempBuffer.resize(samplesPerBlock); // Allocate here
}

2. Ignoring Sample Rate

// BAD - assumes sample rate
float filterCutoff = 1000.0f; // This will sound different at 44.1k vs 96k

// GOOD - calculate based on sample rate
void prepareToPlay(double sampleRate, int samplesPerBlock)
{
    float normalizedCutoff = cutoffHz / sampleRate;
    // Use normalizedCutoff in filter calculations
}

3. Not Handling Different Buffer Sizes

// Your plugin might receive:
// - 64 samples (low latency)
// - 512 samples (normal)
// - 2048 samples (offline render)
// Never assume a fixed size!

Next Steps

Once you've built a gain plugin:

  1. Add more parameters - Try adding a pan control
  2. Learn DSP basics - Filters, oscillators, envelopes
  3. Study existing plugins - Many open-source plugins on GitHub
  4. Read the JUCE tutorials - Excellent documentation

Resources

Conclusion

Building audio plugins combines creativity with technical skill. Start simple, understand the fundamentals, and gradually add complexity.

I built my first plugin as a simple gain like this one. Now I develop professional audio software at Malinow Audio. Every expert started exactly where you are now.

The audio development community is incredibly welcoming. Don't be afraid to ask questions, share your work, and learn from others.

Happy coding, and may your plugins never clip!

Check out my portfolio to see my audio projects, and browse more blog posts for development tips.


Related Articles: