Param Panel

External Store

Bind a slider directly to a minimal, dependency-free reactive store for automatic two-way synchronization.

A control can bind directly to application-owned reactive state instead of owning its value. See External state integration for the full explanation of the binding styles — this example builds the smallest possible store from scratch and binds a slider to it.

Source

A minimal store implementing the ExternalValue<T> shape (get, set, subscribe):

function createStore<T>(initial: T) {
  let value = initial;
  const listeners = new Set<(value: T) => void>();

  return {
    get() {
      return value;
    },

    set(next: T) {
      if (Object.is(value, next)) return; // skip no-op — prevents loops

      value = next;
      for (const listener of listeners) listener(value);
    },

    subscribe(listener: (value: T) => void) {
      listeners.add(listener);
      listener(value);
      return () => listeners.delete(listener);
    },
  };
}

Binding a slider to it:

const thresholdStore = createStore(128);

const threshold = pp.slider({
  label: "Threshold",
  value: thresholdStore,
  min: 0,
  max: 255,
});

thresholdStore.set(180);
console.log(threshold.get()); // 180

threshold.set(200);
console.log(thresholdStore.get()); // 200

Notable details

  • Passing thresholdStore as value — instead of a raw number — is what makes this a ValueSource<T> binding rather than control-owned state.
  • Synchronization is automatic and two-way: setting the store updates the slider's displayed value, and dragging the slider updates the store. Neither side polls the other.
  • The store's set skips notifying when the new value is Object.is-equal to the current one — that equality check is what prevents an update loop between the control and the store (see Avoid synchronization loops).

On this page