Components
Time and sequencing
Blocks
An overdrive as a device you can plug in: a band-pass, a drive, a tone and a mix as a state, a panel that draws the band that reaches the clip, a Web Audio engine and the tools an agent drives it with.
Audio UI
"use client";
import { useState } from "react";A pedal-style distortion. A band-pass picks what is pushed into the soft clip, so the lows do not choke it: the panel draws that band, and the handle on its centre sets the frequency. The width, the drive, the tone and the mix are knobs. It is a device like the compressor: a state, a panel that draws it, an engine that plays it and tools an agent calls, each its own file.
import { OverdrivePanel } from "@/components/audio-ui/devices/overdrive/panel";
import { overdriveDevice } from "@/components/audio-ui/devices/overdrive/device";
const [state, setState] = useState(() => overdriveDevice.defaults());
<OverdrivePanel onValueChange={setState} value={state} />;The state is a plain object keyed by parameter id, so a host stores it, saves it with its project and sends it where it likes. overdriveBand(state) is the band the panel draws.
import { createOverdriveEngine } from "@/components/audio-ui/devices/overdrive/engine";
const engine = createOverdriveEngine(ctx, state);
source.connect(engine.input);
engine.output.connect(ctx.destination);
engine.update(nextState); // parameters ramp: no click
engine.dispose();Only what the band-pass lets through goes into the clip; the clean sound goes round it in the dry path. The drive is a gain before one fixed soft-clip table, and the shaper oversamples 4x.
The shaper oversamples 4x, which delays the signal by 4 ms in Chrome (192 samples). The dry path waits as long, so a partial mix does not comb; the device then has a latency of 4 ms to report to a host. The number was measured in Chrome and the specification does not give it, so measure it in another browser with measureLatency of device-measure.
import { deviceTools } from "@audio-ui/core/device";
import { overdriveDevice } from "@/components/audio-ui/devices/overdrive/device";
const [set, reset] = deviceTools(overdriveDevice);set and reset change the state, with the ranges the device enforces: a value out of range is brought in and reported, a wrong one is refused by name, and a call never throws.
Every control is a native input: the knobs are sliders named after their parameter, with the value and unit as the text value, the switches are toggle buttons, and everything takes the keyboard.