A single circular buffer, a read head chasing a write head, and a fraction of the output folded back on itself. From the tape loops of the Echoplex to juce::dsp::DelayLine, this is the quietest, most fundamental building block in all of audio.
Every echo you have ever heard is the same event reaching your ears twice: once directly, once after a detour. A cliff face, a stairwell, a cathedral — all of them store a copy of the sound for a moment and hand it back late. A digital delay does nothing more exotic. It writes each incoming sample into a long buffer and, some number of samples later, reads it out again.
What makes the delay line interesting is not the single echo but what happens when you feed a portion of the output back into the input. The sound repeats, and repeats, each pass a little quieter than the last, until it sinks below the noise floor. Add a filter in that feedback path and each repeat also grows darker, mimicking the way air and tape lose the high frequencies first. Modulate the delay time and you get chorus, flanging, vibrato and the seasick wow-and-flutter of worn tape. Shorten the delay to a few milliseconds and the discrete echoes fuse into a comb filter — a pitched, resonant colour. The delay line is therefore the seed from which reverb, chorus, flanger, phaser and pitch-shifter all grow.
This page builds a real stereo delay from first principles. The mathematics is worked out honestly, the C++ is idiomatic JUCE, and the live demo below runs a hand-written delay line — sample by sample, in an AudioWorklet — so the algorithm you read is exactly the algorithm you hear. Everything is synthesised in the browser; nothing is a pre-recorded fake.
Store
A circular buffer holds the last few seconds of audio. One pointer writes; another, trailing behind, reads.
Return
A gain $g$ on the feedback path decides how many times the echo repeats before it decays away.
Colour
A filter in the loop and an LFO on the read position turn a plain echo into tape, chorus and dub.
02From spinning tape to a line of code
The delay effect is older than digital audio by decades. Its history is a steady migration of the same idea across ever cheaper and more precise storage media: magnetic tape, an analogue "bucket brigade", and finally RAM.
1950s — tape delay is born
Engineers discover that a tape recorder with separate record and playback heads produces an echo: the playback head reads what the record head laid down a moment earlier. Les Paul, Sam Phillips at Sun Studio ("slapback"), and countless dub engineers build the sound of a decade on this trick.
1959 — Maestro Echoplex
Mike Battle's Echoplex puts a movable playback head on a loop of tape, so the delay time can be dialled in by hand. Its warm saturation and pitch drift become the reference tape-echo tone; the later solid-state EP-3 defines the sound of countless guitar records.
1973 — Roland RE-201 Space Echo
A free-running tape loop with three fixed heads plus a spring reverb. Multiple head taps give rhythmic multi-echo patterns, and the wow, flutter and self-oscillating feedback make it an instrument in its own right.
1969–70s — the bucket brigade (BBD)
Sangster and Teer at Philips invent the bucket-brigade device: a chain of capacitors that passes an analogue sample "hand to hand" on each clock tick. Companded BBD chips (Reticon, Panasonic MN3005) give compact, reliable "oil-can" analogue delay — the heart of the MXR, Boss DM-2 and Electro-Harmonix Memory Man.
1971 — Lexicon Delta-T 101
One of the first commercial digital delays. Audio is converted to numbers, stored in memory and read back — delay time now set by a counter rather than a moving head, with none of tape's drift and hiss.
1980s — the rackmount DDL era
The Lexicon PCM41/PCM42, TC Electronic 2290, Roland SDE-3000 and AMS DMX 15-80 make long, clean, modulated digital delay a studio staple, with the 2290's long delay times and dynamic modulation becoming a signature of the decade's productions.
1990s–2000s — into the DAW
Delay becomes a plug-in. With effectively unlimited memory it can be tempo-synced to the host, made true-stereo and ping-pong, and given per-tap filtering and modulation for free.
2010s — tape emulation returns
Soundtoys EchoBoy, Universal Audio's Echoplex/Space Echo models and Valhalla's delays deliberately re-introduce the wow, saturation and filtered feedback that clean DDLs had removed. The wheel turns: we now spend DSP cycles putting the tape back.
The interpolated delay line is the workhorse of computer music. Nearly every artificial reverberator, chorus, flanger, and physical-model string is built from it.
— after Julius O. Smith III, Physical Audio Signal Processing
03The delay line, the comb, and its teeth
Strip away the tone and modulation and a delay is one line of arithmetic. A pure delay of $D$ samples is the operator that maps an input $x[n]$ to the same signal, later:
Its magnitude response is flat — a pure delay changes no amplitudes, only phase — and the phase is linear, $\angle H(e^{j\omega}) = -\omega D$. In an implementation, $D$ is realised not by shifting an array but by a circular buffer: a fixed block of memory with a write pointer that advances one slot per sample and wraps around at the end. The output is read from the slot $D$ places behind the write pointer.
Adding feedback: the recursive comb filter
A single echo is rarely what we want. Feed a fraction $g$ of the output back to the input and the delayed copy re-enters the line indefinitely:
$$ y[n] = x[n] + g\,y[n-D] $$
Taking the $z$-transform, $Y(z) = X(z) + g\,z^{-D}Y(z)$, and solving gives the transfer function of the feedback (recursive) comb filter:
$$ H(z) = \frac{1}{\,1 - g\,z^{-D}\,} $$
This is an all-pole filter with $D$ poles spread evenly around a circle of radius $|g|^{1/D}$ in the $z$-plane. Its magnitude response is
The denominator is smallest — and the response therefore peaks — whenever $\cos(\omega D)=1$, that is when $\omega D = 2\pi k$. In ordinary frequency the resonances sit at
$$ f_k = k\,\frac{f_s}{D}, \qquad k = 0,1,2,\dots $$
a harmonic series with fundamental spacing $f_s/D$. This regular row of peaks is why the structure is called a comb filter. At the peaks the gain reaches $1/(1-g)$ and at the troughs (where $\cos(\omega D)=-1$) it falls to $1/(1+g)$; the closer $g$ is to one, the taller and narrower the teeth and the longer the echo rings. For a long delay ($D$ large) the teeth are only a few hertz apart and we hear discrete repeats; for a short delay ($D$ a few dozen samples) the teeth are hundreds of hertz apart and we hear a pitched, metallic colour. Flanging and the plucked-string model are simply this same comb with a very short, modulated $D$.
Comb magnitude |H| — drag the sliders. Peaks sit at multiples of fs/D.
Derivation — poles, peak gain and the −3 dB bandwidth of one tooth
The poles of $H(z)=1/(1-g z^{-D})$ solve $z^{D}=g$, giving $D$ roots
so each tooth has bandwidth $\approx (1-g)\,f_s/(\pi D)$ Hz. The quality factor $Q = f_k/\text{BW}$ therefore rises without limit as $g\to1$: high feedback means both a taller peak and a narrower, more sharply pitched tooth.
The feed-forward comb $y[n]=x[n]+g\,x[n-D]$, with $H(z)=1+g\,z^{-D}$, is the complementary structure: it has notches instead of peaks (zeros rather than poles) and cannot ring or self-oscillate. It is the classic flanger topology. Toggle the visualisation above between the two to see peaks turn into troughs.
04Fractional delay: reading between the samples
So far $D$ has been a whole number of samples. But musical delay times, tempo divisions and — crucially — modulation demand a delay of, say, 1323.7 samples. Reading only integer slots would quantise the delay to the sample grid: 44.1 kHz gives a resolution of 22.7 µs, coarse enough to make a swept modulation move in audible steps ("zipper" noise). We need to interpolate between the two buffer slots straddling the fractional read position.
Split the desired delay into integer and fractional parts, $D = M + \eta$ with $0 \le \eta < 1$. The simplest estimate is linear interpolation between the two neighbouring samples:
$$ y[n] = (1-\eta)\,x[n-M] + \eta\,x[n-M-1] $$
Linear interpolation is a first-order FIR filter with $H(z)=(1-\eta)+\eta z^{-1}$. It is cheap and unconditionally stable, but it is a mild low-pass: it attenuates high frequencies (worst at $\eta=0.5$, where it is down about 4 dB at Nyquist) and its delay is slightly frequency-dependent. For an echo whose repeats are already meant to darken, that gentle roll-off is often welcome. Where accuracy matters — long modulated delays, tuned waveguide strings — we reach for higher-order schemes.
Lagrange interpolation
A Lagrange fractional-delay filter of order $N$ fits the unique degree-$N$ polynomial through $N{+}1$ neighbouring samples and evaluates it at the fractional offset. The third-order (cubic) case uses four taps and gives a much flatter magnitude and more accurate phase than linear. The coefficients are
which realises a delay of $\eta$ samples with unity gain at all frequencies. The trade-off is a transient (its own recursion) and delay that varies with frequency away from DC, which makes rapidly modulated allpass delays prone to artefacts. Waveguide reverbs and tuned strings, where the delay changes slowly, favour it precisely because it wastes no high end.
Reading a fractional position between two samples — move the fraction η.
The live demo below reads with linear interpolation and smooths its target delay time, so sweeping the DELAY knob or the modulation LFO glides continuously instead of stepping. The JUCE listing in §7 shows the same idea with DelayLineInterpolationTypes::Lagrange3rd.
05Feedback, tone, tempo and ping-pong
A musical delay is the plain comb filter of §3 dressed with four practical refinements: bounded feedback, a filter in the loop, tempo synchronisation, and a stereo cross-feed.
Why |g| < 1 — and what happens at the edge
Each pass round the loop multiplies the signal by $g$, so the $k$-th echo has amplitude $g^{k}$. The energy of the repeats is a geometric series, and it converges to a finite sum only when $|g| < 1$:
At $|g|=1$ the echoes never decay — the system oscillates — and for $|g|>1$ each repeat is louder than the last and the output grows without bound until it clips. This is exactly the self-oscillation guitarists coax out of a Space Echo. In a plug-in it is a hazard: an accidental $g\ge1$, or a resonant filter in the loop that boosts a band above unity, will run away. Sensible designs cap the feedback control below one (here at 0.98) and place a soft limiter on the loop so that even at the edge the output saturates gracefully rather than exploding. The number of audible repeats is roughly $\log(\varepsilon)/\log|g|$ for an audibility threshold $\varepsilon$; at $g=0.7$ that is about a dozen repeats.
A damped feedback path
Real echoes lose their high frequencies with every bounce. We model this by inserting a one-pole filter $G(z)$ into the loop, so the recursion becomes $y[n] = x[n] + g\,\bigl(G\!*\!y\bigr)[n-D]$ and
$$ H(z) = \frac{1}{\,1 - g\,G(z)\,z^{-D}\,}. $$
A one-pole low-pass $G(z) = \dfrac{a}{1-(1-a)z^{-1}}$ (with $a=1-e^{-2\pi f_c/f_s}$) makes each successive repeat darker — the tape/BBD sound. Swap it for a one-pole high-pass and the repeats thin out instead, keeping the low end clear for a dub-style delay. Because $|G(e^{j\omega})|\le 1$, the filter also improves stability: it holds down the loop gain in the very bands most likely to build up. The TONE control in the demo sweeps this filter's corner and the toggle chooses low- or high-pass.
Tempo synchronisation
A delay locks to a song when its time is derived from the tempo rather than set in milliseconds. One quarter-note lasts $60/\text{BPM}$ seconds, so a note division of $\nu$ quarter-notes gives
with $\nu = 1$ for a quarter, $\tfrac12$ for an eighth, $\tfrac34$ for a dotted eighth (the "Edge" rhythmic delay), and $\tfrac13$ for an eighth-note triplet. Because $D$ is almost never a whole number of samples, tempo-synced delay needs the fractional interpolation of §4 to stay perfectly in time.
Modulation and ping-pong
A slow LFO added to the delay time makes the read head drift, stretching and compressing the replayed audio — this is the wow-and-flutter of tape and, at a few milliseconds of depth, the shimmer of chorus. Ping-pong delay cross-couples the two channels' feedback so each repeat jumps to the opposite speaker:
where $\tilde y$ is the filtered, delayed output and $b_{L,R}$ are the values written into the two circular buffers. Left feeds right and right feeds left, so a single input hops L–R–L–R across the stereo field as it decays. All of this is in the demo.
06Live demo — a hand-written stereo delay
The processor below is a genuine circular-buffer delay line running in an AudioWorklet: linear-interpolated fractional read, a one-pole low-/high-pass in the feedback path, an LFO on the delay time, optional ping-pong cross-feed and a soft limiter to keep runaway feedback in check. Pick a source, press play, and turn things up. Double-click any knob to reset it.
◆ Blue Line — Stereo DelayDSP & MUSIC / AUDIO PLUGINS
300 ms
Delay
40 %
Feedback
4.0 kHz
Tone
0.0 ms
Mod
0.50 Hz
Rate
35 %
Mix
BPM
idle — press play
The three visualisations that follow all describe this delay, from three angles: the impulse response in time, the frequency-domain comb, and the geometry of the circular buffer itself.
Visualisation 1 — the echo train
Send a single impulse into the delay and watch the taps decay. Each stem is one pass round the loop, spaced by the delay time and scaled by the feedback gain (and, in ping-pong mode, alternating between the channels). This is the impulse response $h[n]$ whose Fourier transform is the comb of §3.
Impulse response — stems at k·D with height g^k.
Visualisation 2 — the comb, live
The same magnitude response as §3, but here the frequency axis auto-scales to show roughly two dozen teeth so the fundamental spacing $f_s/D$ stays visible whatever the delay. Longer delays pack the teeth tighter; more feedback sharpens them.
|H(e^jω)| in dB — spacing between teeth = fs/D.
Visualisation 3 — inside the circular buffer
The buffer drawn as a ring. The bright marker is the write head, advancing one slot per sample. The trailing marker is the read head, a delay's-worth behind; the gap between them is the delay time. Turn on ping-pong (or modulation) and a second read head appears — the cross-fed / LFO-wobbled tap. Watch the read head breathe as the modulation sweeps.
Write head chases its own tail; the read head trails by D samples.
07The same delay, in JUCE C++
Two views of the identical algorithm. First a raw circular buffer that maps line-for-line onto the JavaScript above; then the idiomatic version built on juce::dsp::DelayLine with an APVTS and smoothed parameters, which is what you would actually ship.
A raw circular-buffer delay line
// Mono delay with linear-interpolated fractional read and a
// one-pole low-pass in the feedback path. Maps directly to the JS worklet.
class RawDelay
{
public:
void prepare (double sampleRate, int maxDelaySamples)
{
fs = sampleRate;
buf.assign ((size_t) maxDelaySamples + 4, 0.0f);
writePos = 0;
lpState = 0.0f;
}
// delaySamples may be fractional; feedback in [0, 0.98]; lpCoeff = a
float processSample (float x, float delaySamples,
float feedback, float lpCoeff) noexcept
{
const int N = (int) buf.size();
float rp = (float) writePos - delaySamples;
while (rp < 0.0f) rp += (float) N;
const int i0 = (int) rp;
const float frac = rp - (float) i0;
const int i1 = (i0 + 1) % N;
const float y = buf[(size_t) i0] + frac * (buf[(size_t) i1] - buf[(size_t) i0]);
// one-pole low-pass on the signal that is fed back
lpState += lpCoeff * (y - lpState);
// write input + damped feedback, with a soft limiter for safety
float w = x + feedback * lpState;
w = std::tanh (w); // graceful saturation
buf[(size_t) writePos] = w;
if (++writePos >= N) writePos = 0;
return y; // wet (pre-mix) output
}
private:
std::vector<float> buf;
double fs = 44100.0;
int writePos = 0;
float lpState = 0.0f;
};
The read pointer trails writePos by a fractional number of samples; linear interpolation fills the gap.
The idiomatic version — juce::dsp::DelayLine + APVTS
A skewed NormalisableRange (0.3 factor) gives the DELAY and TONE knobs a musical, roughly logarithmic feel.
08Compendium — a taxonomy of delays
The same block, shortened or lengthened and wrapped in feedback, becomes a whole family of effects. The distinction is almost entirely one of delay time and modulation.
Effect
Delay
Feedback
Modulation
Character
Resonator / string
0.05–5 ms
near 1
tuning only
pitched comb; Karplus–Strong pluck
Flanger
1–10 ms
moderate
deep, 0.1–2 Hz
sweeping jet-plane notches
Chorus
15–35 ms
low / none
gentle, multi-voice
thickening, detune shimmer
Slapback
60–120 ms
very low
none
single rockabilly echo
Rhythmic echo
150–800 ms
medium–high
optional wow
tempo-synced repeats, dub
Looper / long
1–30 s
build/hold
none
sound-on-sound layering
Digital vs tape
Clean DDLs kill hiss and drift; tape emulations deliberately restore filtered feedback, saturation and wow. The maths is identical — the difference is what you put in the loop.
BBD reality
Bucket-brigade chips have a fixed number of stages, so delay time is set by clock rate: longer delay means a lower clock and a darker, aliased top end. The characteristic murk of analogue delay is a sampling artefact.
Derivation — the echo density and why short delays sound "pitched"
The impulse response of the feedback comb is a train of unit impulses, $h[n]=\sum_{k\ge0} g^{k}\,\delta[n-kD]$. Its DTFT is the geometric sum
$$ H(e^{j\omega}) = \sum_{k=0}^{\infty} g^{k}e^{-j\omega D k} = \frac{1}{1 - g e^{-j\omega D}}, $$
recovering the transfer function of §3. The time between echoes is $D/f_s$ seconds; the frequency spacing of the comb teeth is its reciprocal, $f_s/D$ Hz. Human hearing fuses echoes closer than roughly 30–50 ms into a single timbral event, so once $D/f_s$ drops below about 25 ms — i.e. $f_s/D$ rises above about 40 Hz — we stop hearing separate repeats and start hearing pitch and colour. That single threshold is the whole difference between an "echo" and a "comb filter".
Practical note — smoothing delay time without pitch glitches
Changing the delay time instantly relocates the read head and produces a click. Smoothing the target (a one-pole glide, or JUCE's SmoothedValue) moves the read head gradually — but moving the read head is a Doppler pitch shift: the classic tape "wow" when you grab the delay knob. Two remedies coexist in real designs: (1) a short crossfade between the old and new read positions (used by many "clean" delays, which pitch-preserve), or (2) embracing the glide as a feature (tape/BBD delays, and this demo, do exactly that). The right choice is aesthetic, not technical.
Related in this collection
Audio Plugins — the hub: the map of the whole series and how these building blocks fit together.
JUCE Tutorial: how a processor, editor, APVTS and the juce::dsp module go together into a plug-in.
Reverb: what you get from dozens of short, filtered, interleaved delay lines — the delay's grandest child.
Modulation: chorus, flanger and vibrato — the same delay line with a very short, LFO-swept time.
09References & further reading
Sources
Smith, J. O. III. Physical Audio Signal Processing — chapters on delay lines and interpolated delay. Stanford CCRMA, online edition. ccrma.stanford.edu/~jos/pasp.
Zölzer, U. (ed.). DAFX: Digital Audio Effects, 2nd ed. Wiley, 2011 — delay, comb filters, modulation and fractional delay.
Dattorro, J. "Effect Design, Part 2: Delay-Line Modulation and Chorus." Journal of the Audio Engineering Society, 45(10), 1997.
Välimäki, V., Pakarinen, J., Erkut, C., & Karjalainen, M. "Discrete-time modelling of musical instruments." Reports on Progress in Physics, 69, 2006 — fractional delay and allpass interpolation.
Laakso, T. I., Välimäki, V., Karjalainen, M., & Laine, U. K. "Splitting the Unit Delay." IEEE Signal Processing Magazine, 13(1), 1996 — the definitive survey of fractional-delay filters.
Smith, J. O. III & Rocchesso, D. "Aspects of Digital Waveguide Networks." CCRMA — the delay line as the core of physical modelling.