Concept

Parameter

A named float you drive at runtime. Subscribers update immediately. Bind it to bus levels, FX values, anything.

Tension rises. You want the music bed up, the ambience down, the low-pass on the drone opening, and the reverb wet going with it. Written by hand that is four setters that have to stay in step, plus a fifth the day someone adds another layer.

A Parameter is a named float you set at runtime, and a set of bindings that map it onto other values. You set the parameter; everything bound to it updates through its own range and curve. It is the seam between an idea a designer has and the ten numbers that idea turns into.

Mental model

parameter "intensity" 0.7 bus("music").level range: 0.4 → 1.0 reverb.wet range: 0.0 → 0.4 filter.frequency range: 400 → 8000 one parameter.set(0.7) → every bound target updates with its mapped curve
One parameter feeds many bound targets. Each binding has its own range + curve.

API surface

Parameter ts
engine.parameter(name, initial?): Parameter

class Parameter {
  readonly name: string;
  readonly value: number;
  set(v: number): void;
  subscribe(fn: (v: number) => void): () => void;
  bindTo(setter: (mapped: number) => void, opts?: {
    from?: number;        // default 0
    to?:   number;        // default 1
    curve?: 'linear' | 'easeIn' | 'easeOut' | 'easeInOut' | 'equal-power';
  }): () => void;
}

Live demo

One slider driving a bus level and a derived value, each through its own range and curve.

Recipes

Get / set

ts ts
const intensity = engine.parameter('intensity', 0.3);
intensity.set(0.7);                       // updates all bound targets

Bind to one or more targets

ts ts
intensity.bindTo((v) => engine.bus('music').level = v, {
  from: 0.4, to: 1.0, curve: 'easeInOut',
});

intensity.bindTo((v) => filter.setFrequency(v), {
  from: 800, to: 12000, curve: 'easeIn',
});

Subscribe for UI updates

ts ts
const off = intensity.subscribe((v) => console.log('intensity:', v));
// later: off();

Pitfalls

Parameters aren't ramps.
A parameter is a discrete value. Subscribers turn it into a ramp on their own, usually by writing to a bus level, which has its own 10 ms ramp inside. When you need a guaranteed N-millisecond crossfade, use a Snapshot.
Don't set in a tight loop.
Each set runs every subscriber synchronously. Driving a parameter from requestAnimationFrame means 60 Hz, which is fine. Anything denser than that, throttle it.

Related

  • Snapshot handles discrete mix states, crossfaded.
  • Bus is the most common bind target.