Blog / Sound Design

Karplus-Strong: How a Two-Point Average Accidentally Invented Physical Modeling

Karplus-Strong: How a Two-Point Average Accidentally Invented Physical Modeling

In 1983, Alex Strong was experimenting with wavetable synthesis on an 8-bit microcomputer at Stanford. His idea was simple: replace each sample in a circular buffer with the average of itself and its neighbor, then loop the buffer. The result sounded eerily like a plucked string, regardless of what noise you started with. Kevin Karplus analyzed why it worked, and together they published "Digital Synthesis of Plucked String and Drum Timbres" in Computer Music Journal (vol. 7, no. 2, 1983).

What they had stumbled on was not just a cheap synthesis trick. It was, as Julius O. Smith later proved, an exact physical model of a vibrating string, derived by accident from the world's simplest possible lowpass filter.

The Algorithm in Three Lines

The Karplus-Strong algorithm has three components:

  1. A circular buffer of N samples, initialized with white noise (or any short waveform).
  2. A feedback loop that reads from the buffer and writes back a filtered version.
  3. A one-zero lowpass filter: y[n] = (y[n-N] + y[n-N-1]) / 2.

That's it. The output is read from the buffer on each sample, and each position is replaced by the average of itself delayed by N and N+1 samples. The buffer length N sets the fundamental frequency: N = Fs / F0, where Fs is the sample rate and F0 is the desired pitch. At 44100 Hz, a low E string at 82.4 Hz requires a buffer of about 535 samples.

The filter's transfer function is:

H(z) = (1 + z^{-1}) / 2

This is a single-zero FIR filter. Its magnitude response is 1 at DC (0 Hz) and 0 at Nyquist (Fs/2). In other words, it attenuates high frequencies on every pass through the loop.

Why This Makes a String Sound

The key insight, the one that eluded Karplus and Strong but that Julius Smith identified, is what this filter represents physically.

When a real string vibrates, it loses energy to friction, air resistance, and the bridge. Crucially, it loses high-frequency energy faster than low-frequency energy. A plucked guitar string's 10th harmonic decays much quicker than its fundamental. This is why plucked strings sound bright immediately after the attack and gradually grow rounder and warmer.

The one-zero lowpass does exactly this. On every cycle through the delay loop (one period of the fundamental), it attenuates the high-frequency content slightly. The feedback structure ensures this happens repeatedly, compounding the effect: harmonics near Nyquist die out in a few cycles, while the fundamental takes thousands of cycles to fade. The decay time is automatically frequency-dependent in exactly the way a real string decays.

Smith, working at CCRMA at Stanford, identified the Karplus-Strong structure as a special case of digital waveguide synthesis, a method that models acoustic wave propagation in strings and tubes using bidirectional delay lines. The K-S loop represents a traveling wave completing one round trip along the string per period. The filter represents the total energy loss that wave accumulates over that trip.

Strong had accidentally implemented a wave equation solver.

The Tuning Problem

There's a subtle issue with N = Fs / F0: this only gives integer delay lengths, and most frequencies don't land on integers. At 44100 Hz, an A4 at 440 Hz requires exactly 100.23 samples, you can't have a fractional delay line with a simple circular buffer.

The original algorithm accepted the nearest integer and accepted the resulting pitch error. The Extended Karplus-Strong (EKS) algorithm, published in the same 1983 CMJ issue by David Jaffe and Julius Smith, addressed this with a fractional delay allpass filter tacked onto the loop. By tuning a first-order allpass to shift the total loop delay by a fraction of a sample, arbitrary pitches become possible within normal tolerances.

The Extended Algorithm: Three Additions

Jaffe and Smith's 1983 extensions addressed the three most audible limitations of the original:

Decay stretching. The original lowpass gives a fixed relationship between pitch and decay time, low notes sustain much longer than high notes. This is physically realistic but compositionally inflexible. EKS introduces a stretching coefficient S that weights the two-point average asymmetrically:

H_d(z) = (1 - S) + S * z^{-1}

When S = 0.5, you get the original average. As S approaches 1.0, the filter becomes a pure delay (no lowpass), and the string sustains forever. As S approaches 0, the filter becomes a constant multiplier and the string decays almost instantly.

Pick position. A real string sounds different when plucked near the bridge versus the midpoint. Plucking at the bridge produces a bright, cutting tone because the string's motion is small but its curvature is large, emphasizing high harmonics. Plucking near the midpoint cancels those harmonics and produces a hollow, fundamental-heavy sound.

EKS models this with a comb filter inserted into the excitation path:

H_β(z) = 1 - z^{-floor(β * P)}

where β ∈ (0,1) is the normalized pluck position and P is the period in samples. This creates a null at frequencies whose half-wavelengths equal the pick distance, exactly what a real string does.

Excitation waveform. The original uses flat white noise. EKS allows any short waveform: a filtered noise burst, a single impulse, or a recorded pluck attack. The choice of excitation has almost no effect on the sustained tone (the loop filter determines that), but it dramatically shapes the attack transient that listeners actually use to identify an instrument.

Drums: Replace the Lowpass with an Allpass

The same loop structure produces drum-like sounds when the lowpass is replaced by an all-pass filter. An allpass preserves all frequencies at equal amplitude but shifts their phases by different amounts, introducing inharmonicity. The resulting tones are pitched but detuned, with a characteristic metallic, drum-like character. Vary the period after the initial attack and you get the falling pitch of a tom or kick drum.

A Minimal C++ Implementation

class KarplusStrong {
public:
    KarplusStrong(float sampleRate, float freq) {
        int N = static_cast<int>(sampleRate / freq + 0.5f);
        buffer.assign(N, 0.0f);
        for (auto& s : buffer)
            s = (rand() / float(RAND_MAX)) * 2.0f - 1.0f;
    }

    float tick() {
        float out = buffer[pos];
        float next = (buffer[pos] + buffer[(pos + 1) % buffer.size()]) * 0.5f;
        buffer[pos] = next;
        pos = (pos + 1) % buffer.size();
        return out;
    }

private:
    std::vector<float> buffer;
    size_t pos = 0;
};

The buffer initialization fills N samples with noise. Each call to tick() reads the current sample, writes the two-point average back, advances the pointer, and returns the output. At 44100 Hz, this produces 44100 of these per second, essentially free on any modern processor.

What Karplus-Strong Left Behind

The algorithm directly influenced digital waveguide synthesis, which powers the physical modeling engines in instruments like the Yamaha VL1 (1994) and underlies many modern guitar and string simulations. Julius Smith's CCRMA research group extended waveguide techniques to bowed strings, woodwinds, brass, and percussion throughout the 1990s.

The specific insight worth keeping: a one-zero filter inside a feedback delay loop is a wave equation. This is not a coincidence or an approximation, it is what the wave equation becomes when you discretize it. Strong found this by accident, Karplus explained it mathematically, and Smith proved it physically. The path from "averaging two numbers in a loop" to "exact physical model of string vibration" is shorter than anyone expected.