Build and distribute professional VST3/AU audio plugins with BeatConnect's complete development platform.
Before diving into the code, you'll need a BeatConnect developer account:
- Sign up at beatconnect.com
- Request Developer Access from your account settings
- Create a new project in the developer dashboard
Once approved, you'll have access to:
- Cloud Build Pipeline - Automated compilation for Windows & macOS
- Code Signing - Automatic signing for trusted installs
- License Activation - Built-in copy protection with machine fingerprinting
- Distribution - Hosted downloads with Stripe payment integration
Check your dashboard at beatconnect.com for current build quotas and plan limits.
This SDK is designed to work seamlessly with AI coding assistants like Claude Code. Whether you're new to plugin development or just want to move faster, AI can help you scaffold, debug, and iterate on your plugins.
- Clone this SDK to your machine
- Open in Claude Code or your preferred AI-enabled IDE
- Use the built-in skills to scaffold and modify your plugin
| Skill | What it does |
|---|---|
/bc-create-plugin |
Interactive guide to build your first plugin - no C++ knowledge required! |
Here are some ways to work with Claude on your plugin:
Starting a new project:
/bc-create-plugin
Then just follow the conversation - Claude will ask what kind of plugin you want to build.
Adding features:
Add a wet/dry mix parameter to my plugin with a range of 0-100%
Debugging:
My plugin crashes when I load it in Ableton - help me debug the initialization
UI work:
Create a vintage-style knob component for the saturation control
Learning:
Explain how the JUCE 8 WebSliderRelay system works
The SDK includes a CLAUDE.md file with detailed context about the codebase, so AI assistants can understand the architecture and patterns automatically.
- Web/JUCE 8 Hybrid Architecture - React UI + C++ audio processing with native relay system
- Activation System - License validation with machine fingerprinting
- Preset Management - Factory and user preset save/load system
- Asset Downloads - Download samples, presets, and content from BeatConnect
- Build Pipeline Integration - Automated compilation and code signing
- Distribution - Signed downloads, Stripe payments
Once you have developer access, you can start building locally:
Option A: Use GitHub Template (Recommended)
- Go to the BeatConnect Plugin SDK repository
- Click "Use this template" → "Create a new repository"
- Name your repo and create it (this gives you a clean single-commit history)
Option B: Fork the SDK
- Fork the repository on GitHub
- Clone your fork locally
Add the required topic so BeatConnect can discover your plugin:
- Go to your repository on GitHub
- Click the ⚙️ gear icon next to "About" (top right of repo page)
- Under "Topics", add:
beatconnect-plugin - Click "Save changes"
Your repo has this structure:
your-repo/
├── beatconnect-sdk/ # SDK (don't modify)
├── plugin/ # YOUR PLUGIN CODE GOES HERE
│ ├── CMakeLists.txt
│ ├── Source/
│ └── web-ui/
└── CLAUDE.md
To customize your plugin, edit files in plugin/ and replace placeholders:
{{PLUGIN_NAME}}→ YourPluginName{{PLUGIN_NAME_UPPER}}→ YOUR_PLUGIN_NAME{{COMPANY_NAME}}→ YourCompany{{PLUGIN_CODE}}→ Ypln (4 chars){{MANUFACTURER_CODE}}→ Ycom (4 chars)
Or use Claude Code with /bc-create-plugin to scaffold everything automatically!
cd plugin/web-ui
npm install
npm run build
cd ../..cd plugin
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release- Windows:
plugin/build/YourPluginName_artefacts/Release/VST3/ - macOS:
plugin/build/YourPluginName_artefacts/Release/VST3/andAU/
If you need to pull updates from the upstream SDK, always use --squash to keep your commit history clean:
# Add upstream remote (only once)
git remote add upstream https://github.com/BeatConnect/beatconnect_plugin_sdk.git
# Pull updates with --squash (keeps your history clean)
git fetch upstream
git merge --squash upstream/main
git commit -m "chore: update SDK to latest"Important: Never use a regular git merge upstream/main - it will pollute your repo with the entire SDK development history.
License activation with machine fingerprinting. No manual configuration required - credentials are automatically injected by the BeatConnect build system.
When you build through BeatConnect, the build system injects project_data.json into your plugin's resources folder with all necessary credentials. Your code just needs to call createFromBuildData():
#include <beatconnect/Activation.h>
// In your PluginProcessor constructor or loadProjectData():
#if BEATCONNECT_ACTIVATION_ENABLED
// Auto-configured from project_data.json injected by BeatConnect build
activation_ = beatconnect::Activation::createFromBuildData(
JucePlugin_Name, // Plugin name for debug logging
false // Disable debug logging in production
);
if (activation_) {
DBG("BeatConnect Activation SDK initialized");
} else {
DBG("Running in development mode (no build data)");
}
#endif
// In your UI layer, check activation status:
if (activation_ && !activation_->isActivated()) {
showActivationDialog();
}
// Activate with user-entered code (UUID format supported)
auto status = activation_->activate(userEnteredCode);
if (status == beatconnect::ActivationStatus::Valid) {
// Success! State is automatically saved.
}For local testing without the BeatConnect build pipeline:
beatconnect::ActivationConfig config;
config.apiBaseUrl = "https://xxx.supabase.co"; // SDK adds /functions/v1 internally
config.pluginId = "your-project-uuid";
config.supabaseKey = "your-publishable-key";
config.pluginName = "MyPlugin";
config.enableDebugLogging = true;
auto activation = beatconnect::Activation::create(config);Important Notes:
- The SDK does NOT perform network validation during creation. This is intentional - many DAWs (Ableton, Logic) reject plugins that make network requests during initialization.
- Each plugin instance should own its own
Activationinstance (NOT a singleton) to avoid conflicts when multiple plugin instances are loaded. createFromBuildData()returnsnullptrwhen running in development without build data.
Each Activation instance has its own debug logging:
// Enable via createFromBuildData second parameter
activation_ = beatconnect::Activation::createFromBuildData("MyPlugin", true);
// Or via config for manual setup
config.enableDebugLogging = true;
// Log messages (thread-safe)
activation_->debugLog("Starting activation...");
activation_->debugLog("Result: " + statusString);
// Show user where logs are (opens folder in file manager)
if (activation_->isDebugEnabled()) {
activation_->revealDebugLog();
}
// Get log path
std::string logPath = activation_->getDebugLogPath();Log file locations:
- macOS:
~/Library/Application Support/BeatConnect/<pluginName>/debug.log - Windows:
%APPDATA%/BeatConnect/<pluginName>/debug.log - Linux:
~/.local/share/BeatConnect/<pluginName>/debug.log
Download samples, presets, and other content:
#include <beatconnect/AssetDownloader.h>
beatconnect::DownloaderConfig config;
config.apiBaseUrl = "https://xxx.supabase.co/functions/v1";
config.downloadPath = "/path/to/assets";
config.authToken = userToken;
beatconnect::AssetDownloader downloader;
downloader.configure(config);
// Download with progress
auto [status, filePath] = downloader.download(assetId,
[](const beatconnect::DownloadProgress& p) {
updateProgressBar(p.percent);
});User and factory preset management with C++/React integration:
#include "PresetManager.h"
// In your processor
PresetManager presetManager(apvts, JucePlugin_Name);
// Define factory presets
presetManager.setFactoryPresets({
"Default", "Clean", "Warm", "Bright"
});
// Save user preset
presetManager.saveUserPreset("My Custom Sound");
// Load preset
presetManager.loadPreset("My Custom Sound", false); // false = user preset
presetManager.loadPreset("Default", true); // true = factory preset
// Get JSON for UI
juce::String json = presetManager.getPresetListAsJson();// React: Use the preset hook
import { usePresets } from './hooks/usePresets';
function PresetSelector() {
const {
factoryPresets,
userPresets,
currentPreset,
savePreset,
loadPreset,
deletePreset
} = usePresets();
return (
<select onChange={(e) => loadPreset(e.target.value, false)}>
{userPresets.map(p => (
<option key={p.name} value={p.name}>{p.name}</option>
))}
</select>
);
}User presets are stored as XML files in:
- macOS:
~/Library/Application Support/<PluginName>/UserPresets/ - Windows:
%APPDATA%/<PluginName>/UserPresets/ - Linux:
~/.local/share/<PluginName>/UserPresets/
The SDK is already included in beatconnect-sdk/. Your plugin's CMakeLists.txt should include:
# Include BeatConnect CMake helpers
include(${CMAKE_SOURCE_DIR}/../beatconnect-sdk/cmake/BeatConnectPlugin.cmake)
# Fetch JUCE automatically
beatconnect_fetch_juce()
# After defining your plugin target:
beatconnect_configure_plugin(${PROJECT_NAME})This automatically handles JUCE setup, WebView configuration, and platform-specific settings.
your-repo/
├── beatconnect-sdk/ # SDK (don't modify these files)
│ ├── cmake/ # CMake helpers
│ ├── activation/ # License activation SDK
│ ├── templates/ # File templates for reference
│ ├── example-plugin/ # Reference implementation
│ └── docs/ # Documentation
├── plugin/ # YOUR PLUGIN (edit these files)
│ ├── CMakeLists.txt # Build configuration
│ ├── Source/
│ │ ├── PluginProcessor.cpp # Audio processing (C++)
│ │ ├── PluginProcessor.h
│ │ ├── PluginEditor.cpp # WebView UI host + JUCE 8 relays
│ │ ├── PluginEditor.h
│ │ └── ParameterIDs.h # Parameter ID constants
│ ├── web-ui/ # React UI
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── App.tsx
│ │ │ ├── main.tsx
│ │ │ ├── lib/
│ │ │ │ └── juce-bridge.ts # JUCE 8 frontend API
│ │ │ └── hooks/
│ │ │ └── useJuceParam.ts # React hooks for params
│ │ └── vite.config.ts
│ └── Resources/WebUI/ # Built web assets (auto-generated)
├── CLAUDE.md # AI assistant instructions
└── .github/workflows/
└── build.yml
┌─────────────────────────────────────────────────────┐
│ DAW (Ableton, Logic, FL Studio, etc.) │
└─────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ Plugin (VST3/AU) │
│ ┌─────────────────┐ ┌───────────────────────────┐ │
│ │ PluginProcessor │ │ PluginEditor │ │
│ │ (C++ DSP) │◄─┤ (WebView + React UI) │ │
│ │ │ │ │ │
│ │ • processBlock │ │ • JUCE 8 Relay System │ │
│ │ • Parameters │ │ • useSliderParam hooks │ │
│ │ • State save │ │ • Visualizations │ │
│ └─────────────────┘ └───────────────────────────┘ │
└─────────────────────────────────────────────────────┘
Unlike postMessage-based approaches, JUCE 8 provides native relay classes for bidirectional parameter sync:
| C++ Class | TypeScript | Use Case |
|---|---|---|
WebSliderRelay |
useSliderParam() |
Float parameters |
WebToggleButtonRelay |
useToggleParam() |
Bool parameters |
WebComboBoxRelay |
useComboParam() |
Choice parameters |
// C++: Create relays BEFORE WebBrowserComponent
gainRelay = std::make_unique<juce::WebSliderRelay>("gain");
// Register with WebView options
auto options = juce::WebBrowserComponent::Options()
.withNativeIntegrationEnabled()
.withOptionsFrom(*gainRelay);
// Connect to APVTS
gainAttachment = std::make_unique<juce::WebSliderParameterAttachment>(
*apvts.getParameter("gain"), *gainRelay, nullptr);// TypeScript: Hook syncs automatically
const gain = useSliderParam('gain', { defaultValue: 0.5 });
<input
type="range"
value={gain.value}
onMouseDown={gain.onDragStart}
onMouseUp={gain.onDragEnd}
onChange={(e) => gain.setValue(parseFloat(e.target.value))}
/>-
Start web dev server:
cd plugin/web-ui && npm run dev
-
Build plugin in dev mode:
cd plugin cmake -B build -DYOUR_PLUGIN_NAME_DEV_MODE=ON cmake --build build -
Run Standalone target - connects to localhost:5173 for hot reload
-
Build web UI:
cd plugin/web-ui && npm run build
-
Build plugin:
cd plugin cmake -B build -DCMAKE_BUILD_TYPE=Release cmake --build build --config Release
Once your plugin builds locally, connect it to BeatConnect's cloud pipeline:
- Create a project in your BeatConnect developer dashboard
- Link your GitHub repo - BeatConnect will provide
PROJECT_IDandWEBHOOK_SECRET - Add secrets to your GitHub repo settings
- Push to trigger builds - BeatConnect compiles, signs, and hosts your plugin
Your dashboard shows build status, download stats, and license activations in real-time.
See docs/integration.md for detailed setup instructions.
A complete, buildable example demonstrating all correct patterns:
- JUCE 8 WebView parameter binding
- Level metering and visualizers
- Activation system integration
- State persistence
cd beatconnect-sdk/example-plugin
# Build web UI
cd web-ui && npm install && npm run build && cd ..
# Build plugin
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release- CLAUDE.md - Instructions for Claude Code to scaffold new plugins
- beatconnect-sdk/docs/architecture.md - Detailed architecture guide
- beatconnect-sdk/docs/integration.md - BeatConnect integration guide
- beatconnect-sdk/docs/best-practices.md - Best practices & validation checklist (common issues and fixes)
- CMake 3.22+
- JUCE 8.0.4+ (fetched automatically)
- Node.js 18+ (for web UI)
- Windows: Visual Studio 2022
- macOS: Xcode 14+
MIT