Feedback delay DSP tutorial with a playable example
A delay line, a feedback path, and an index that wraps instead of faulting.
Anyone who wants an echo — or the beginnings of a reverb.
An echo is a conversation with an earlier sample. Write today's input into a circular buffer, then read the place where an older sample was stored. Feed some of that old sample back into the buffer and the conversation repeats.
The array provides the memory; a write position says where “now” is. Its wrapped indexing lets the position move around the buffer without copying any samples.
Controls#
| Parameter | Default | Range | Meaning |
|---|---|---|---|
time | 0.29 s | 0.005–0.65 s | Requested spacing between echoes, limited by the buffer capacity. |
feedback | 0.55 | 0–0.92 | Fraction of an echo written back for the next repeat. |
mix | 0.46 | 0–1 | Dry/wet blend: 0 is the input, 1 is only the delayed signal. |
level | 0.8 | 0–1 | Gain applied after the blend. |
Both endpoints are mono. A short sound or an impulse makes the repeats easiest to follow.
The complete patch#
processor Echo
{
input stream float in;
param float time = 0.29f [0.005f, 0.65f] smooth 30.0f;
param float feedback = 0.55f [0.0f, 0.92f] smooth 20.0f;
param float mix = 0.46f [0.0f, 1.0f] smooth 20.0f;
param float level = 0.8f [0.0f, 1.0f] smooth 20.0f;
output stream float out;
float line[65536];
float pos = 0.0f;
void main()
{
loop
{
let delaySamples = clamp(int(time * processor.frequency), 1, 65535);
let w = int(pos);
let r = w - delaySamples;
let delayed = line[r];
line[w] = in + delayed * min(feedback, 0.92f);
let next = pos + 1.0f;
pos = next > 65535.0f ? 0.0f : next;
out <- (in * (1.0f - mix) + delayed * mix) * level;
advance();
}
}
}Follow one trip around the buffer#
- Convert
timefrom seconds to a sample count using the current sample rate. Clamp it between 1 and 65,535 so it fits this buffer. - Read
line[w - delaySamples]. Wrapped indexing finds the older sample even when the subtraction is negative. - Write
in + delayed * feedbackat the current position. An echo at feedback 0.5 returns next time at half its previous amplitude. - Advance the write position, then blend the current input with the delayed sample. The buffer begins at zero, so there is no echo until it has history.
The array has 65,536 samples. At 96 kHz it holds about 683 ms, so the entire 5–650 ms control range fits at 44.1, 48 and 96 kHz. Clamping to its capacity prevents a long requested delay from wrapping into a short, unintended one.
Try it#
Start with feedback at 0 to hear one repeat. Raise it gradually to hear a
tail develop. Then move time while audio is running: this example uses an
integer read position, so smoothing the time control does not provide
fractional-sample interpolation. The chorus
shows how to interpolate a moving delay.
If it is silent at first: at mix = 1, the dry sound is removed. You must
wait one delay time before the first input reaches the output.
Next: a reverb, which is a great many of these at once.