VOLTAlearn

Learn Volta / Write your own DSP

A plucked string

Build a Karplus–Strong string from a noise burst, a delay line and a loss filter.

lesson 38 of 39 · about 12 minutes

Open this lesson in the workbench →

Karplus and Strong found in 1983 that a burst of noise, fed into a delay line one period long and recirculated through a gentle lowpass, sounds like a plucked string. It is the smallest physical model there is — a delay line, an average and a gain — and every piece of it is something this act has already taught.

The starter's burstLaw is the pluck alone: for one period after the note starts (noteTime < 1.0 / hz) it passes white noise through, scaled by vel; after that, silence. A click per note. noteTime and vel are voice reads — inside a processO law they are plain numbers for this note.

Give the law a line instead of a cell: dline 50ms in the init, a handle buf in the binders. Read it one period back — d = sampleRate / hz samples — and add what you read to the burst: y = burst + g * dtap buf d. Push y back in as the line's next state. The burst now goes round and round, once per period: a pitched tone at exactly the note's frequency.

Average two neighbouring taps, 0.5 * (dtap buf d + dtap buf (d + 1.0)): that is the loss filter. It damps the high harmonics a little on every trip, so the tone darkens as it decays, like a real string. It also delays by half a sample, so read at d = sampleRate / hz - 0.5 to stay in tune. The gain g = exp (-6.9 / (decay * hz)) makes the ring fall 60 dB in decay seconds whatever the pitch.

Wrap it as oscillator string (decay: Float = 2.0) = \hz -> hz |> processO (ksLaw decay) (dline 50ms) noise and call string (freq pitch) { decay = 3.0 }. The finished, tuned version with a brightness control and deterministic noise is ksPluck in osclib — open it in the Reference panel and compare.

The starting point#

The lesson opens with this song in the editor. Every edit recompiles and plays.

-- A plucked string (Karplus–Strong): the law below only makes the pluck —
-- one period of noise at each note start (`noteTime` is seconds since the
-- note began). Feed that burst into a delay line one period long and let
-- it recirculate, a little quieter each time, and it becomes a string.
burstLaw = \hz n s ->
  let burst = if noteTime < 1.0 / hz then n * vel else 0.0 in
  [burst, s]

pluck = patch {
  source = freq pitch |> processO burstLaw 0.0 noise,
  ampEnv = adsr 1ms 3s 0 200ms,
  gain   = 0.25
}

tune = play pluck !"e3 b3 e4 g4 b4 g4 e4 b3"

What to watch#

Watch the scope: the starter draws one short burst per note; with the line and the loop each burst turns into a repeating waveform that slowly smooths into a sine as the loss filter eats its harmonics.

Goals#

The workbench checks these as you work; the solution is there when you want it.

  1. Give the law a delay line and read it one period back
  2. Close the loop: the loss filter, a gain, and the output pushed back in
  3. Name it: oscillator string (decay: Float = 2.0) = …
  4. Stretch: damp the string when the key lifts

Make it yours#

Replace the white-noise burst with a filtered one (a one-pole on n), or pluck with a single impulse instead of noise. How does the attack's colour change the whole note?

Further reading#

DSP cookbook in the reference.