VOLTAreference

Reference / For contributors

One audio engine across hosts

For contributors

Open in the workbench →
On this page · 3 sections

For readers comparing hosts: the shared audio engine contract and the historical compatibility differences it replaced.

The policy (historical)#

The native engine (engine/) is the reference sound of Volta. When a DSP decision trades audio quality against matching the browser runtime, the native engine takes the quality. The browser runtime (runtime/src/voice.ts + the worklets) is the documented compatibility mode: it remains fully playable and musically equivalent, but where it has not yet adopted a native upgrade its output is the legacy sound, not the reference.

What this changes in practice:

  • "Verbatim port of the worklet" is no longer a design constraint on engine/src/. Parity comments in the engine describe history, not an invariant to preserve.
  • New DSP lands native-first. Each native-only behavior gets a row in the table below when it ships, with a note on what porting it to the browser would take. Removing a row = the port landed.
  • Determinism guarantees are unchanged: both backends remain bit-deterministic with themselves. Cross-backend bit-equality was never promised and is now explicitly out of scope; cross-backend musical equivalence (same notes, same envelope times, same mix topology) is still required.
  • Golden IR snapshots are unaffected — the IR is shared. Divergence lives strictly in how each backend renders a node.
  • A/B against the legacy sound = play the same song in the browser build.

Native-only behaviors (the porting ledger) — HISTORICAL#

Historical as of 2026-07-24. These rows were written as a porting ledger against the deleted Web Audio graph; their "browser (compat)", "to port" and "effort" columns were dropped 2026-09-06 because there is one backend. What remains is the DSP rationale each row records — the native implementation is simply the implementation.

#BehaviorNative implementation
1Exponential ADSR segmentsdsp::adsr_value shapes attack as a normalized concave RC rise and decay/release as convex RC falls (K = 4.6, exact arrival), replacing all-linear segments. The release is floored at dsp::MIN_RELEASE = 2 ms (2026-08-04, DSP audit E5): r = 0 used to return exactly 0 at note-off — a 0.107 step on a 0.175-peak sustain, 17.8× the sustain's own p99 |Δx|, which the ~10 Hz voice DC blocker turned into a thump. A sub-floor release now renders bit-identically to a 2ms one; anything ≥ 2 ms is untouched
2Retrigger-from-current-value envelopesthe Adsr node is stateful per voice; a glide-voice retrigger ramps the attack from the envelope's current value (no click, analog behavior). Envelopes anchor on Voice.env_anchor, never start_sample — that one gates lo
3True periodic wavefolder with ADAAfold is an unclamped periodic triangle fold with SECOND-order MinusX antiderivative anti-aliasing (stateful, per channel; un-folded signal passes bit-exactly one sample late — 2026-08 audit #1)
4Unclamped, ADAA drive & clipdrive applies its (optionally biased) tanh(k·(x+b))/tanh(k) to the raw input (no pre-clamp), and both drive and clip use SECOND-order MinusX antiderivative anti-aliasing (stateful, per channel — the same machinery as #3; linear part passes undrooped at one sample of latency, output clamped to the transfer's true range; per-sample feedback-region arms stay first-order Direct so loops don't detune). A nonzero drive bias adds even harmonics
5Per-voice DC blockera 10 Hz one-pole high-pass per voice channel on the stereo render path (kills pulse-duty / folder DC thumps at note edges)
6Zero-delay-feedback ladderthe Moog ladder resonance loop is solved zero-delay (linearized TPT solve, tanh on the input path, full 1+k passband compensation; the opt-in ladder … { makeup } rescales it to 1+m·k, so m = 0 exposes the bare ladder's resonance loss). With drive > 0 the native core additionally runs the FULL nonlinear four-transistor-stage ladder on the DAFx-21 paper-37 port-Hamiltonian non-iterative solve (ladder_tick_ph; provably zero-input-stable discrete-gradient step, 2026-08 audit A7)
7Per-sample biquad design under modulationlpf/hpf/bpf keep one coefficient set for a block whose cutoff and Q are static; a moving cutoff/Q redesigns the RBJ section at every sample (2026-08 audit F6 — the earlier start/end coefficient lerp left fast envelopes with a −16 dB null and block-grid FM sidebands)
8FDN reverb|> reverb decay mix renders through an 8-line feedback delay network (Householder feedback + zita-style in-loop allpasses, mutually-prime delays ≈25–63 ms with a doubled table past 2.5 s decays, per-line THREE-band decay — bass blooms 1.5×, mids 0.45×, top octaves ≈0.29× — split L/R input diffusion feeding even/odd lines — the input image survives into the tank — slow fractional-read modulation on 3 lines against metallic long decays, decorrelated L/R taps; decay = RT60) (engine/src/fdn.rs)
9Osc sync + corner rounding (|> sync master ["hard"|"soft"], |> soften a)Osc carries an optional sync master input (hard reset, polyBLEP-corrected; triangle reset also BLAMP-corrected — 6.3a; the SINE reset instead gets the closed-form all-orders La Pastina/D'Angelo residual on a cubic B-spline kernel, dsp::sine_sync_residual — no added latency), a sync_mode ("soft" = window-gated reset, 6.3b), and soften (0..1 Juno-DCO reset-RC corner rounding, 6.5 — also available as a standalone Soften { input, amount } node when the piped source is not a bare Osc, e.g. as3340 … |> soften a; the two spellings render bit-identically)
10FM operator phase-mod input (fmOp … |> phaseMod m d, and sine … |> phaseMod m d which promotes to the same operator)FmOperator carries an optional phase_mod signal (cycles), enabling true PM operator chains (DX-style), not just freq-summing FM
11Wavetable mip crossfadethe oscillator lerps the two mips bracketing the continuous level log2(fs·hz/sr) — no brightness step at octave boundaries on a glide
12Band-limited sample readsthe plain sample player, stretch streaming, chop and granular grain reads all go through the rate-tracking Kaiser-sinc kernel (resample::sinc_read_for); only a NaN rate falls back to 4-point Catmull-Rom (dsp::hermite4). Since 2026-08-06 (audit X1/X7 and X1's D1 review) that covers sub-unity rates — the 44.1 kHz-bank-at-48 kHz case, i.e. most playback — and unity, whose fractional-phase reads the 4-point kernel notched
13PolyBLAMP trianglethe triangle's corner discontinuities at phase 0/0.5 get 2-sample polyBLAMP residuals (B(s)=(1−|s|)³/6 scaled by the ±8·dt slope change)
14Macro smoothinglive SetMacro (knob/shred) chases a ~15 ms one-pole, rendered as a within-block linear ramp by MacroRef; the control-side set_macro convenience snaps (offline renders start on target)
15Voice stealingvoices = N is enforced per synth before the full 64-voice arena ceiling; either tier steals released-nearest-end, else oldest (glide voices last). A steal settles through a ~2 ms exponential declick and every voice fades its last ~5 ms before the lifetime gate (no end-of-tail step)
16Additive Nyquist fadepartials fade linearly over the top 15% below Nyquist instead of switching off (break) — no pop/timbre step when a glide crosses a partial out
17True-stereo per-voice granulareach grain keeps its equal-power pan into a real L/R pair (the granular Sample node originates stereo in compute_stereo)
18Oversampled nonlinear filter loop (2× / 4×, per model)when filter drive > 0, the native tick runs oversampled with a per-model factor selected by measurement (circuit-fidelity Stage G): ladder/diode/korg35 at 4× (upsample → four inner ticks → two-stage allpass-IIR half-band decimate; the ladder upsamples through a halfband anti-imaging interpolator, diode/korg35 through cubic-Hermite; korg35 was promoted from 2× on 2026-08-05, audit F2), svf at 2× (linear-interp upsample → two inner ticks → one-stage half-band) — the *_OS_FACTOR consts in engine/src/voice/filters.rs. Cutoff g warps for factor·sr. All four clear the −60 dB alias budget on the §4.3 isolated-nonlinearity probe (see Filters: shape brightness and resonance § Oversampling & latency). A clean filter (drive = 0) stays single-rate and bit-identical — except that a ladder/diode/korg35 at normalized resonance ≥ 0.8 oversamples its loop even at drive = 0 (2026-08 DSP audit #6: near self-oscillation the loop nonlinearities limit-cycle)
19PatternMod anti-pop glidenumeric-pattern modulators glide ~5 ms at each step boundary (a pure function of transport time — block-size independent, identical across voices)
20Plate decay is a true RT60plate.rs maps decay via the closed form g = 10^(−3·T_loop/(4·rt)) over the tank's loop delay (0.9 safety cap ≈ 12 s max); the old (1 − 0.3/rt).clamp(0.4, 0.9) made every decay ≤ 0.43 s identical and rang ~4× nominal at the top
21Tape echo first repeat through the loop pathTapeLine::tick returns the damped+saturated read (playhead-is-output): echo #1 already sounds like tape
22Dynamics de-zipper + two-stage optical releasecompressor/optical/FET gains slew per-sample toward the control-rate target, each with τ = exactly its own hold (16 samples / 16 / 4 — the FET recomputes 4× finer, so it slews 4× faster); the optical release blends fast (0.1 s) + slow (1.5 s) cells in dB — real two-stage recovery
23De-zippered k-rate parametersmodulated per-voice delay time/feedback, comb time (fractional-sample read — comb is now in TUNE as a resonator), waveshaper amounts, formant morph and eq3 gains all ramp across the block; haas slews its delay (τ 5 ms)
24diode — TB-303 diode ladderfour unbuffered one-pole stages loading each other (Pirkle/mystran closed-form ZDF solve, staggered G1..G4 poles), an input tanh clipper on the solved loop, a ~6 Hz AC-coupling HPF in the feedback (the 303 series cap), self-osc at k≈17 pre-warped (DIODE_TUNE) so the resonant peak tracks the nominal cutoff, drive-gated 4× oversample (engine/src/voice/filters.rs::diode_tick)
25korg35 — MS-20 Sallen-KeyTPT LP+HP one-poles in a positive-feedback loop, K 0..2, a fixed-pivot feedback self-limiter (Kₑ = K·tanh(K·yₚ)/(K·yₚ)) → bounded self-osc near K≈2; "lp" 12 dB/oct, "hp" the 6 dB/oct hybrid; drive-gated 4× oversample (engine/src/voice/filters.rs::korg35_tick)
26bbd amount "i"|"ii"|"i+ii" — Juno chorus modesthe mode-set bbd runs the Juno recipe (engine/src/voice/modfx.rs::bbd_juno_sample): a triangle LFO on the clock period (square-wave pitch deviation, delay ~1.5–5.5 ms), no compander, two cascaded ~8 kHz LPs + a ~10 Hz HP, a seeded-noise hiss floor scaled by wet depth, and a stereo output (right LFO inverted in i/ii, in-phase in i+ii; measured L/R correlation ~0.05). The generic mode-less bbd is unchanged
27adsr … punch — analog attack overshootthe optional trailing punch (0..1) aims the RC attack past the target (overshoot 1 + punch·0.3, output clamped at 1) so the front edge is steeper (dsp::adsr_value); punch = 0 is bit-identical to the plain RC attack
28Through-zero FM (signed oscillator frequency)an fm modulator deep enough to drive Osc/Pulse freq below 0 runs the phase accumulator backwards through the crossing (p -= p.floor() wraps both directions; the PolyBLEP step uses |dt| so it stays correct under reverse motion) — the TZFM clang. Positive-freq renders bit-identical (engine/src/voice/eval.rs Osc/Pulse arms)
29Tape hysteresis (|> tape feedback-path magnetization)the tape echo's feedback saturation is a one-state hysteretic shaper (TapeHysteresis, engine/src/effect.rs, analog-fidelity 6.6): a magnetization state relaxes toward tanh(k·x)/k, the relaxation widening + turning asymmetric with the drive direction as level rises (genuine memory — measured loop area at moderate drive, collapsing to ≈memoryless at low level), a trailing DC blocker, driven-alias floor ≈−54 dB
30Spring reverb (|> spring tension decay [mix])a phenomenological spring tank (engine/src/spring.rs, analog-fidelity 6.7): two slightly-detuned tanks (L 100 / R 88 first-order allpasses) in feedback delay loops + in-loop LP — the descending dispersion chirp (HF-faster, measured ≈38 ms spread) with a decorrelated stereo field
31as3340 "tri"|"saw"|"pulse" freq [pw] [character] — AS3340/CEM3340 circuit modela full tri-core VCO model (engine/src/as3340.rs, analog-fidelity addendum): direction-switched triangle integration, a derived saw with the mid-ramp tri-core glitch/spike, a comparator pulse with hysteresis + PWM latch, causal 3-tap four-point cubic-B-spline BLEP/BLAMP (existing 1-sample alignment retained; 2-point high-rate fallback; clean ~2 kHz alias floors saw −37.7 / pulse −42.3 / triangle −64.2 dB), an analog expo converter (scale/curve trim error + HF flattening) driven at block rate, and per-sample drift/wobble/jitter. character (0..1) scales the imperfections; the per-voice chip identity + noise seed are drawn from rng::draw (deterministic, replay bit-identical)
32BBD circuit layer (circuit-fidelity Stage B)all native BBD paths integrate the CLOCK for their tap positions (voice/modfx.rs::bbd_clock_step — pitch shift ∝ clock derivative, transit-time asymmetry); generic bbd + bbdDelay run an NE570-style syllabic compander (ne570_compress/expand, τ=20 ms, feedback-sensed comp / feedforward expand); bbdDelay filters track clock/3 (MN3005-class N=4096) and leak a clock tone when the clock is in-band (−54 dB source × the reconstruction bank'sH(f_clk)≈ −38 dB — the hardware's leak passes through the output filter); BbdChorus hiss is coordinate-seeded (module seed + track)
33Tape circuit pass (circuit-fidelity Stage C)TapeLine runs the physical chain: record-amp tanh → true Jiles-Atherton magnetization (JaHysteresis: trapezoidal + fixed-cap Newton, f64 state, 2× + halfband, build-time unity calibration, engine-side bias) → 30 Hz coupling-cap HP → tape; playback gap-loss LP + 60 Hz/+2.5 dB head bump on the wet tap; wow & flutter is coordinate-seeded capstan+pinch (shared L/R) + independent per-channel 40 Hz cubic-interpolated scrape motion
34vca control [character] — modeled gain cell (circuit-fidelity Stage D)the native Vca arm (voice/eval.rs, §5.3) renders input · control at character = 0 and, as character rises (blend min(1, character·2.5)), an OTA/Blackmer cell: tanh at a shrinking headroom (6→1, = 4.0 at c=0.4), seeded control feedthrough (the high-passed control dg/dt, at a per-voice "vca-feed" draw over −50…−70 dBFS — fast envelopes thump, slow ones don't), and a gain-tracking noise floor (white·character·DEPTH·(0.25+0.75·g)). Stereo-aware; deterministic (coordinate-seeded)
35svf … [drive] [character] — OTA character mode (circuit-fidelity Stage I)the native Svf arm (voice/filters.rs::svf_tick_ota, §5.2) runs the linear ZDF SVF at character = 0 and, at character > 0, the SEM/Prophet OTA cell: an input-attenuator OTA (fast_tanh(x/s_in)·s_in, s_in = 0.7/c — distorts earlier/creamier, odd/H3-dominant), a resonance-path OTA on the bandpass integrator (s = 0.3/c) that bounds resonance to a clean on-tune sine, resonance loss + a self-osc bias folded into k (k·(1+c·0.30·k) − c·0.02), and a seeded LM13700 noise floor (white·c·0.0012 at the OTA input, so it rides the cell); OTA engages 2× oversampling (alias floor −79 dB lp / −66 dB mode @ c=1/drive=1, clears the −60 budget). character = 0 is bit-identical to the shipped SVF
36Band-limited, Nyquist-clamped LFOs (2026-08-04, DSP audit E8)CompiledNode::Lfo renders lfoSine/lfoSaw/lfoSquare/lfoTri through dsp::osc_sample_bl — the same polyBLEP/BLAMP kernels the audio oscillators use — and clamps the rate at 0.45·sr like Osc/Pulse/FM (audit B6). The shapes accept any Hz or signal rate, so an audio-rate AM/ring-mod LFO is a supported idiom; it used to alias like a naive oscillator (lfoSaw −29.1 dB at 50 Hz, −12.7 at 2 kHz) and a rate past Nyquist silently WRAPPED (30 kHz played at 18 kHz). Now −54.9 / −36.5 dB, +21.7…+26.5 dB at every rate. lfoSine is bit-identical, and the residual is zero away from a corner, so ordinary LFO rates are untouched but for the sample straddling the wrap
37Plate diffuses the input PER CHANNEL (2026-08-06, DSP audit R1)plate.rs runs one copy of Dattorro's input chain (bandwidth one-pole + four series diffusers) per channel and feeds each tank half from one of them, figure-8 crossfeed unchanged — so a stereo image survives into the tank, as it already did in the FDN (#8). It used to mono-sum: measured wet level −5.9 dB at interchannel correlation ρ=+1 but −9.0 at ρ=0 (ordinary wide stereo), −16.0 at ρ=−0.8 and exactly zero at ρ=−1, and hard-left/hard-right sources produced bit-identical reverb. Now −5.92 dB at every ρ; a mono source renders BIT-IDENTICAL to the old path (both chains are the same filter), so only stereo input behaviour moved. The L chain drives the r branch because Dattorro's output accumulators cross — that is what puts a hard-left source's reverb on the left (+3.0 dB same-side)

Pre-existing divergences (documented before this policy)#

  • Shred/one-shot default-bus voices: native routes them through master+limiter; the browser sends them to ctx.destination past both.
  • Glide of pitch: native glides every use of pitch (a keytracked filter follows the slide); the browser glides only the freq port.
  • Effect tails across recompile: converged (2026-07-10). Both backends now preserve an unchanged chain's state across a recompile — the browser via the effects-JSON rebuild key, native via whole-chain reuse in the hot-swap (FxReuse, Hot Swap §4.5). Chains binding sample-bank buffers (convolve/spectralMorph/sampled granularize) still rebuild on native so late-loading samples and re-baked bounces re-resolve — and every rebuild (both backends) crossfades out over 80 ms instead of cutting the tail.
  • PingPong/Delay channel topology: small documented differences in down-mix points (see effect.rs comments).

Native gaps (browser-only features — the reverse direction)#

Both are closed. Input (live mic/line) is a real CompiledNode::Input fed by the engine's LiveInput hub (Live audio input), and warp source-BPM lock landed 2026-07-24 (SampleBank::bpm, set by set_sample_bpm; the Sample/Stretch path warps by project_bpm / source_bpm).

Workflow#

There is nothing to port. A native DSP change belongs in the feature's own docs/reference/<feature>.md, with ARCHITECTURE.md updated when the system-level behavior changed. This page is closed to new rows.