VOLTAreference

Reference / Design a sound

Additive synthesis

Design a sound · playable examples

Open in the workbench →
On this page · 4 sections

Build a tone from individual sine-wave partials, then let their strengths and frequencies change independently.

Hear it and change it#

Listen for: a steady, hollow tone made by adding selected sine-wave partials. There is no filter removing the missing harmonics; they are absent from the recipe.

Listen to this example · four bars, then the tail
tempo = 120bpm
timingVersion = 2
drawbars = patch {
  source = additive (freq pitch) [1, 0, 0.5, 0, 0.25],
  ampEnv = adsr 8ms 0ms 0.8 100ms,
  gain = vel * 0.25
}
play drawbars "c4 eb4 g4 bb4"

Make it yours: put 0.4 in the second slot. That adds the second harmonic, one octave above the fundamental. Additive synthesis lets those partials move independently, too.

What it is#

A live additive oscillator: a bank of sine partials summed live, where every partial's amplitude — and optionally its phase and frequency ratio — is its own control signal. The evolving counterpart of the wavetableOf table (Wavetable synthesis) — a wavetable bakes a fixed spectrum, additive lets each harmonic move while the note sounds.

Surface#

-- a fixed drawbar registration (static spectrum)
organ = additive (freq pitch) [const 1, const 0, const 0.7, const 1]

-- harmonics that breathe: each partial on its own LFO / envelope
swell = additive (freq pitch) [
  const 1,
  lfoSine @ 1/5c |> scale [0, 0.6],
  adsr 300ms 400ms 0.3 300ms * 0.4,
]

-- per-partial phase offset: each entry is an [amp, phase] pair
hollow = additive (freq pitch) [[const 1, const 0], [const 0.5, const 0.25]]

-- inharmonic bank: [ratio, amp, phase?] — the Risset-bell surface
bell = additiveAt (freq pitch) [
  [0.56, const 1   |> withDecay 1.6s],
  [0.92, const 0.7 |> withDecay 1.2s],
  [1.19, const 0.6 |> withDecay 0.9s],
]

-- Railsback stretch: bend a harmonic bank sharp in the top
epiano = additive (freq pitch) [const 1, const 0.6, const 0.4] |> inharmonic 0.0009

-- procedural spectra: a generator instead of an explicit list
sawish = additive (freq pitch) (sawSpectrum 64)
pw     = macro 0.35                                           -- `width` is a builtin
pwm   = additive (freq pitch) (pulseSpectrum 48 pw)
vox   = additive (freq pitch) (formantSpectrum 96 [700Hz, 1100Hz, 2600Hz])
center = lfoSine @ 1/8c |> scale [1, 4]                       -- center in octaves
shep  = additive (freq pitch) (gaussSpectrum 96 center 1.2)

-- per-partial drift: one oscillator → a choir
solina = additive (freq pitch) (sawSpectrum 48) |> ensemble 0.06

-- additive resynthesis: analyse a sample, replay it repitched
voice = additiveOf (sample "ah-vowel") (freq pitch)

additive : Sig Control Hz -> (Hole) -> Sig Audio Bipolar. The first argument is the fundamental frequency (freq pitch, plus any + nCents / + fm … terms — it routes exactly like an oscillator's freq port). The second argument is either:

  • an explicit partial list — 1..=32 entries, each an amplitude signal or an [amp, phase] pair (a mix is allowed; bare entries default phase to 0); or
  • a procedural spectrum generator (sawSpectrum count, squareSpectrum, triangleSpectrum, pulseSpectrum count width, formantSpectrum count [Hz…], gaussSpectrum count center bw) — 1..=512 partials. pulseSpectrum's width, gaussSpectrum's center and its bw all take a live control signal, so a Gaussian bump can wander and breathe at once.

additiveAt freq [[ratio, amp, phase?], …] is the inharmonic sibling (1..=64 partials). |> inharmonic B Railsback-stretches a harmonic bank; |> ensemble depth adds per-partial drift (the depth may be a live macro or control signal — the string machine's half of the Vintage/slop knob, see below); |> tuned [amount] snaps every partial's pitch class onto the ambient tuning's cents lattice — timbre matched to intonation, and the one post-op that also works on a spectrum generator (expanded to an explicit table) and on additiveOf (Match timbre to tuning); additiveOf (sample …) freq resynths a recording — either spelling of its sample slot, a bank string or an inline sample "…", at a fixed 48 partials (a compile-time constant with no surface port; a bounceOf/sampleOf/comp handle is not accepted here). Spectral morph is automatic: morph a b t of two explicit additive banks blends them per partial into one oscillator.

Showcase songs: examples/songs/additive.volta and examples/songs/additivepro.volta (extended synthesis examples).

Notes & limits#

  • The old browser caps are gone with the Web Audio compat path — the engine renders full spectra and moving centres identically on every target.
  • Explicit lists: ≤ 32 partials (additive), ≤ 64 (additiveAt). Procedural spectra: ≤ 512 partials.
  • The recurrence is harmonic-only. Inharmonic banks (additiveAt, |> inharmonic, |> ensemble) pay for a phase accumulator per partial.
  • Resynthesis is native-only and offline. A harmonic STFT analysis estimates the fundamental by autocorrelation and tracks count harmonic amplitude/phase frames; there is no live microphone → additive (that would be a real-time phase vocoder). Analysis is currently per-voice on the first block — fine for held notes; repeating the analysis for many notes can increase processing cost.
  • Band-limiting is automatic and free everywhere: partials above Nyquist are not summed, and the native engine fades the top 15 % so a glide doesn't pop.