DOCS / AUDIO & NATIVE
Audio parameters
Parameter binding is two-way and automation-safe. C++ owns the truth (the AudioProcessorValueTreeState); JS reads, sets, and subscribes through vsreact::ParameterBridge. All values are normalized 0..1.
useParameter(id)
const gain = useParameter("gain");
gain.value // 0..1, live — updates when the DAW automates it
gain.text // host-formatted display text, e.g. "-3.2 dB"
gain.name // parameter name from the APVTS
gain.label // unit label, e.g. "dB"
gain.defaultValue // the host's normalized default — double-click reset target
gain.begin(); // start an automation gesture
gain.set(0.75); // ...as many times as you like (drag)
gain.end(); // end the gesture — hosts record clean automationAlways bracket drags with begin()/end() — that is what makes host automation recording and touch/latch modes behave. The built-in controls do it for you.
Ready-made controls
Every built-in control has a Param* twin that wraps useParameter with correct begin/set/end gestures and takes its label from the parameter name:
<ParamKnob paramId="gain" size={88} />
<ParamSlider paramId="mix" width={220} />
<ParamSlider paramId="level" vertical height={140} /> // fader
<ParamToggle paramId="bypass" /> // on = value ≥ 0.5
<ParamXYPad paramX="cutoff" paramY="resonance" /> // two params, one drag
<ParamSegmented paramId="shape" options={["SINE", "SAW", "SQR"]} />
<ParamSelect paramId="mode" options={MODES} /> // dropdown for long lists
<ParamNumberBox paramId="freq" /> // host text, drag/wheel/reset
<ParamCheckbox paramId="oversample" /> // bool, settings-panel style
<ParamRadioGroup paramId="os" options={["OFF","2X","4X"]} />
<ParamMacroPad paramX="granulation" paramY="deepFx" /> // the Output-style centerpiece
<ParamHardwareKnob paramId="drive" /> // skeuomorphic cap + notch
<ParamCrossfader paramId="mix" /> // DRY/WET strip- ParamToggle — bool-style parameters; a click writes a full begin/set/end gesture so hosts record it cleanly.
- ParamXYPad — drives two parameters at once; both gestures open on drag-start and close on release.
- ParamSegmented — choice-style parameters; the normalized value maps to an option index (
index / (count − 1)), matchingAudioParameterChoice.
The unbound versions (Knob, Slider, Toggle, XYPad, Segmented) are exported too, for values that are not host parameters — UI zoom, tab selection, list filters.
The one-line editor
useParameterList() enumerates every parameter in the APVTS (via param:list), which makes a complete, working editor exactly one line:
import { render, GenericEditor } from "@vsreact/core";
render(<GenericEditor />);<GenericEditor columns? size? trackColor? valueColor? /> lays out one ParamKnob per parameter in rows — the fastest path from a processor to a usable UI, and a solid starting point you can replace control by control. For custom generic UIs, build on the hook directly:
const params = useParameterList();
// [{ id, name, label, value, text }, …] — one entry per APVTS parameter
return params.map((p) => <ParamSlider key={p.id} paramId={p.id} />);C++ wiring
vsreact::ParameterBridge bridge { processor.apvts };
// 1. chain param:* calls first in onNativeCall
options.onNativeCall = [this] (const juce::String& name, const juce::var& args) -> juce::var {
if (auto handled = bridge.handleNativeCall (name, args))
return *handled;
return {};
};
// 2. attach the RootView so DAW-side changes reach useParameter
bridge.attach (*root);DAW-side changes (automation, preset loads, other UI) are coalesced on the message thread — the bridge is a juce::AsyncUpdater, so a burst of automation becomes one batched push per message-loop tick — and delivered to JS as param events. The useParameter hook subscribes for you.
The wire protocol
| MESSAGE | PAYLOAD | DIRECTION |
|---|---|---|
param:list | {} → [{id, name, label, value, text}, …] | JS → C++ (enumeration) |
param:get | {id} → {value, text, name, label} | JS → C++ |
param:set | {id, value} | JS → C++ |
param:begin / param:end | {id} | JS → C++ (gesture brackets) |
param event | {id, value, text} | C++ → JS |