VSTDOCSv0.0.17

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)

TSX
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 automation

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

TSX
<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)), matching AudioParameterChoice.

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:

ui/src/main.tsx — an entire plugin UI
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:

TSX
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

PluginEditor.h / .cpp
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

MESSAGEPAYLOADDIRECTION
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