|
iPlug2 - C++ Audio Plug-in Framework
|
iPlug2 supports compiling plugins to WebAssembly (WASM) for running in web browsers. This guide covers the modern split DSP/UI architecture.
The WASM build system creates two separate modules:
| Module | Thread | Purpose |
|---|---|---|
| DSP | AudioWorklet | Audio processing, runs in real-time audio thread |
| UI | Main | IGraphics rendering, user interaction |
Communication between modules uses postMessage for parameters/MIDI and optionally SharedArrayBuffer for low-latency visualization data.
Add source ~/emsdk/emsdk_env.sh to your shell profile for persistence.
For SharedArrayBuffer support (needed for visualization data), your server must send these headers:
Without these headers, the plugin will fall back to postMessage for all communication (higher latency for visualization).
Each example project has a makedist-wasm.sh script:
The script:
SINGLE_FILE=1 (BASE64 embedded)PLUG_HAS_UI=1)build-web-wasm/For plugins without IGraphics (PLUG_HAS_UI=0), only the DSP module is built. The template auto-generates parameter controls.
From the iPlug2 repository root:
For faster local UI iteration, use the fast UI preset. This keeps the build type at Release so the DSP module still uses the normal release optimization, but builds the Wasm UI module with -O0:
By default the CMake Wasm UI uses IPLUG2_WASM_UI_OPTIMIZATION=-O3 for release builds. Set -DIPLUG2_WASM_UI_OPTIMIZATION=-O0 directly if you want the fastest compile/link path in a custom build directory.
CMake targets:
IPlugEffect-wasm-dsp - DSP moduleIPlugEffect-wasm-ui - UI moduleIPlugEffect-wasm-dist - Full distribution bundleCreate config/YourPlugin-wasm.mk:
Create separate project files for DSP and UI modules:
**projects/YourPlugin-wasm-dsp.mk**:
**projects/YourPlugin-wasm-ui.mk**:
DSP and UI communicate via typed messages:
| Type | Fields | Description |
|---|---|---|
param | paramIdx, value | Parameter change |
midi | status, data1, data2 | MIDI message |
sysex | data (ArrayBuffer) | SysEx message |
arbitrary | msgTag, ctrlTag, data | Custom message |
tick | - | Idle tick (flush queued messages) |
| Verb | Fields | Description |
|---|---|---|
SPVFD | paramIdx, value | Parameter value from DSP |
SCVFD | ctrlTag, value | Control value (visualization) |
SCMFD | ctrlTag, msgTag, data | Control message |
SAMFD | msgTag, data | Arbitrary message |
SSMFD | data | SysEx from DSP |
pluginInfo | data | Plugin metadata (params, channels) |
For high-frequency visualization data, SCVFD/SCMFD/SAMFD can use a ring buffer in SharedArrayBuffer:
The build copies templates from IPlug/WEB/TemplateWasm/:
| File | Purpose |
|---|---|
index.html | Main page with Web Audio setup |
scripts/IPlugWasmBundle.js.template | Controller class, connects DSP↔UI |
scripts/IPlugWasmProcessor.js.template | AudioWorkletProcessor wrapper |
styles/style.css | Default styling |
Placeholders like NAME_PLACEHOLDER are replaced with the plugin name during build.
When an IGraphics control opens a platform popup menu in a web build, iPlug2 renders it with the browser's HTML Popover API. The generated menu supports submenus, pointer input, Escape dismissal, and keyboard navigation with arrow keys and Enter/Space. A custom IGraphics popup menu control attached with AttachPopupMenuControl() still overrides the web platform menu.
Add CSS custom properties to the template stylesheet, or to any stylesheet loaded by the host page:
For more specific styling, the DOM uses these class hooks: .iplug-popup-menu, .iplug-popup-menu__panel, .iplug-popup-menu__item, .iplug-popup-menu__check, .iplug-popup-menu__submenu-indicator, .iplug-popup-menu__title, and .iplug-popup-menu__separator.
Both DSP and UI modules log to the browser console. DSP messages are prefixed with the plugin name.
**"SharedArrayBuffer is not defined"** Server missing COOP/COEP headers. Plugin will work but visualization data uses slower postMessage path.
**"Module not found in globalThis"** DSP module failed to load. Check browser console for WASM compilation errors.
Audio glitches DSP module may be too heavy. Profile with Chrome DevTools Performance panel. Consider:
ProcessBlockPython 3 with COOP/COEP headers:
Save as serve.py and run: python3 serve.py 8080
The WASM DSP module supports multiple plugin instances running in the same AudioWorklet context. This enables:
Each AudioWorkletProcessor creates its own DSP instance:
The C++ side maintains an instance registry:
createInstance() - allocates new plugin, returns IDdestroyInstance(id) - cleans up instanceinstanceId as first parameterModule.createInstance()port for postMessage callbacksModule.destroyInstance(instanceId)Each instance has its own:
The WASM module code is shared across all instances, but each instance has isolated state.
| Feature | WASM (Split) | WAM |
|---|---|---|
| SDK dependency | None | WAM SDK required |
| Architecture | Split DSP/UI | Combined |
| AudioWorklet | Native | Via SDK |
| Visualization | SAB + postMessage | WAM events |
| DAW integration | Basic | WAM host support |
Use WASM Split for: standalone web plugins, embedding in web apps, simple deployment.
Use WAM for: DAW integration, WAM-compatible hosts, standardized plugin format.