Transfer Curve
transfer-curveViewersInput amplitude across, output up. For a memoryless nonlinearity that is not a diagram of the effect, it is the effect — the whole of a distortion stage's behaviour is this one line, and the gap between it and the dashed identity diagonal is the distortion at true scale. Computed rather than measured, so it is right before a single sample plays. Three treatments over one plot: the curve for a soft-clip drive, a staircase for a bitcrusher that draws the quantiser's real decision boundaries instead of faking diagonals through them, and a bar row naming which harmonics a Chebyshev order produces — measured off the very curve above it. A fourth shaper draws an electric-piano pickup, whose distance sets how far the transfer bends and whose alignment decides whether it produces even harmonics or only odd ones; that one runs the other way round from the rest, in that the audio path's waveshaper table is filled from the same function this plots rather than the plot being transcribed from shipped audio. Over any of them, feed it samples and the stretch of the curve the signal is currently exercising is brightened, which is the question a static curve cannot answer: not what the stage would do at any amplitude, but which amplitudes are arriving.
The dashed diagonal is the signal untouched; the gap to the curve is the distortion, at true scale. The bright stretch is the part of the curve a swelling tone is exercising, measured from real samples.
Install
Copies the source into your project. Anything it depends on comes with it.
$ npx shadcn add @syntho-ui/transfer-curvePulls in
First time? Add the registry to your components.json once:
{
"registries": {
"@syntho-ui": "https://engine.syntho.app/r/{name}.json"
}
}Usage
Controlled or uncontrolled, whichever suits the surrounding state.
import { TransferCurve } from "@/components/ui/transfer-curve"
// A soft-clip drive, with the live operating region lit from an analyser.
<TransferCurve shaper={{ kind: "drive", amount: 0.4 }} analyser={analyser} />
// A bitcrusher: treads and risers, not a polyline through them.
<TransferCurve shaper={{ kind: "crush", bits: 4 }} />
// A Chebyshev order, with the harmonics it produces under the plot.
<TransferCurve shaper={{ kind: "chebyshev", order: 5 }} showHarmonics />
// An electric-piano pickup: distance sets how hard it bends, alignment
// sets whether it makes even harmonics or only odd ones.
<TransferCurve shaper={{ kind: "pickup", distance: 0.2, symmetry: 0.4 }} />Props
| Prop | Type | Default | Description |
|---|---|---|---|
| shaper | { kind: "drive"; amount } | { kind: "crush"; bits; quantizer?; rateRatio? } | { kind: "chebyshev"; order } | — | The curve to draw, and what picks the default treatment. Each variant is the formulation the matching audio stage runs, pinned bit-identical to it in the lib's tests. |
| min / max | number | -1 / 1 | The amplitude window both axes cover. |
| staircase | boolean | — | Draw treads and risers. Defaults to true for a crush shaper and false otherwise — the only honest default, since a staircase over a continuous curve invents levels and a polyline over a quantiser erases them. Ignored above 128 treads, where the curve is drawn instead. |
| showHarmonics | boolean | — | Show the harmonic-weight bar row. Defaults to true for a chebyshev shaper. Bars in antiphase are drawn hollow rather than rectified. |
| harmonicAmplitude | number | 0.85 | Amplitude of the sine the row is measured with. At exactly 1 a Chebyshev order produces one harmonic and nothing else; just below it the n, n-2, n-4 ladder appears, which is what real material gets. |
| maxHarmonics | number | 24 | Most bars to draw. The strongest partials win — truncating by harmonic number would empty the row exactly where it matters, since a high order's only partial is a high one. |
| analyser | AnalyserNode | null | — | Live input samples for the operating-region overlay. Read-only. |
| getData | (target: Float32Array) => boolean | void | — | Fill the frame with input samples each frame — the getData form the kit's other live displays take, so a worklet or a test harness can drive the overlay with no Web Audio involved. |
| region | { min, max } | null | — | The exercised region supplied directly — the driven form. Takes precedence over any live source. |
| regionDecay | number | 1.2 | How fast the region's edges fall back toward silence, in amplitude per second. The measurement has no memory; without a decay the overlay flickers on anything but a steady tone. |
| color | string | — | Override the accent colour for this instance. |
| height | number | 220 | Plot height in pixels. The plot stays square inside it. |
Interaction
- Drag with pointer capture, so the gesture keeps tracking past the edge of the control.
- Shift while dragging for a fine pass; release it mid-gesture and coarse tracking resumes.
- Double-click resets to
defaultValue. - Arrow keys step normally, Shift+arrow steps finely, PageUp/PageDown jump by ten, Home/End go to the extremes.
- Proper
sliderandswitchroles with livearia-valuetext, so a screen reader announces the formatted value rather than the raw number. - The scroll wheel adjusts the value while the pointer is over the control; stepped ranges move at least one declared step per notch by default.