Feedback
Why a cycle needs a delay you write yourself, and what it costs.
Anyone building a reverb, a comb, or anything that listens to itself.
A cycle with nothing in it has no execution order — every node in it needs a value that does not exist yet — so it is rejected. Put a frame of delay on one edge and the loop is well defined:
connection {
in -> mixer.a;
mixer.out -> dly.in;
dly.out -> [1] mixer.b; // one frame late; this is the loop
dly.out -> out;
}A delayed edge reads a frame that has already been computed, so it imposes
no ordering at all — which is exactly why it breaks the cycle. [1] through
[64] are available; longer than that is a delay line and belongs inside a
processor, where its memory is visible.
Read the delay in samples#
source -> [1] destination delivers the previous frame's value. At 48 kHz,
that is about 20.8 microseconds, not one millisecond. It is enough to make the
execution order possible, but much too short to create an ordinary echo.
A long musical delay belongs in a processor buffer. The graph's delayed edge then has a different job: it makes an inter-node feedback cycle explicit. Also consider the gain around the cycle. A valid graph can still produce a signal that grows without bound if the feedback algorithm does not attenuate or otherwise control it.
Why you write the delay instead of the compiler#
Where it sits changes what the loop sounds like. A compiler that quietly inserted one would be choosing your feedback path for you, and you would not be able to see which edge it picked.
What it costs#
Real, and inherent: a graph with a loop is rendered one sample at a time, not a block at a time. Node A's input at frame f depends on node B's output at f−1, and B runs after A, so B's block does not exist when A's does.
--bench-graph reports +0% for block rendering on such a graph, which is the
truth rather than a rounding.
Note
Feedback graphs require the node-by-node execution path; they do not have
a fused graph entry. The current browser runtime does not support that path.
Render these examples locally with polec, or use the exported C runner.