In principle, doing this might be expected to cause an infinite loop, with the LFO stuck referring to both its own envelope and its own RATE in order to compute its own RATE. What is actually happening here? How many iterations of self-reference does Serum execute? What value does it feed into the "earliest" iteration (a.k.a., the most abstract iteration; a.k.a., the most distant iteration from the one which actually modifies the sound wave)?
Let me paraphrase how I think you think it works.
Let's say the LFO module is a function f(R,T)
meaning the LFO's output at time T, with rate R. And because of the feedback loop, rate R is the LFO's output, you assume that this must mean an infinite recursion where the LFO's output would have to be calculated as f(f(f(f(f(f(...),T),T),T),T),T). But that's not how its done in actual systems.
I don't have Serum, and I think relatively few people on this site have it either. Even fewer have Serum's source code. I most certainly don't. But I'm fairly sure that Serum doesn't process its output like that. Modular DSP systems where you can connect the inputs and outputs of signal-processing components or "nodes" (such as oscillator, envelope generator, filter, gain, delay) are programmed so that for each sample - or a whole buffer worth of samples - the signal path is processed in some order, once per node for each sample (or batch of samples).
As pseudo code, the processing logic is very simple:
for each node: node.process()
Nodes do not call other nodes. There is no recursion, regardless of how inputs and outputs are routed.
Each node's "process()" function produces one output value (or a batch of values if processing is done in "buffers" to reduce overhead) for each of its outputs (it may have several distinct outputs). An output value is a number that can be stored in a variable inside the node instance's memory. The process() function performs its calculations by using its inputs, which are also numbers. The numbers come from whatever is routed to the inputs in the signal path. You can think of signal routing arrows as pointers or references to variables. An input routing is a pointer to a number variable somewhere in memory. (The pointer points backwards compared to what you'd draw in a arrow drawing.)
The signal path is a directed graph, and a good processing order is to run the process() functions in decreasing root distance in the graph. The system's master outputs are the "root" node of the graph. The first nodes to be processed are ones that are the furthest from the root.
Here's an example routing with an External Audio Input, an LFO, a Filter and a Master Audio Output node. And the LFO's output is routed to its own Rate input.
The root distance order places these nodes in an order where
- (Root distance 2) the External Audio Input and the LFO are process()'ed first, in any order or even simultaneously in parallel, but let's say they're 1 and 2 like in the picture,
- (Root distance 1) the Filter node is done next,
- (Root distance 0) and finally the Master Audio Output.
What to do with cycles in the graph? You have to resolve cycles somehow and determine that there's a feedback loop from the LFO's output to its Rate input, and you ignore that in the root distance calculation.
What happens in the LFO module's process() function? What is given into its Rate input, since its source hasn't been calculated yet? Infinite recursion? No. The previous value of the Output is read into the Rate input, meaning that it gets whatever was calculated for the previous sample. In essence, there is a delay of one sample (or more if processing is done in larger than 1-sample buffers) in the feedback loop. When the LFO.process() function has done its thing, the result is stored in a variable, overwriting the number there was previously.
For parts of the signal path which don't have feedback loops or cycles in the graph, there is no delay. The Master Audio Output for sample of time T results directly from a Filter'ed External Audio Input of time T. (Ignoring that the Filter itself might have some delay in it.)
Is there an intuitive way to think about how (or even a precise formula to put into graphic software) to modify a sound wave up to a certain number of such iterations?
By "iteration" you mean recursion, but there's no actual recursion between processing functions, as I explained above. The process() functions of node objects do not call the process() functions of their input or output target nodes, and they don't call themselves either. Maybe if a node is a sub-module container for other nodes, then its process() function can call its children.
Caveat emptor: anyone with access to Serum's source code is welcome to post a more educated answer.