Blog / Development

Wave Digital Filters - The Hidden Math Behind Analog Modeling Plugins

Wave Digital Filters - The Hidden Math Behind Analog Modeling Plugins

If you have ever looked at the math behind analog modeling, you have probably seen the bilinear transform. Every DSP textbook starts there: replace s with (2/T)·(1-z⁻¹)/(1+z⁻¹), push your analog transfer function through, and out comes a digital filter. It works fine for textbook circuits like a single op-amp filter where the topology is already a clean transfer function on paper.

The trouble starts the moment your circuit has feedback through a nonlinear element - a triode, a diode clipper, a JFET. The bilinear transform produces a digital filter with a delay-free loop: the output at sample n depends on the input at sample n through a nonlinear function. There is no closed-form way to evaluate it. You can iterate (Newton-Raphson at every sample), or you can insert a one-sample delay and accept the resulting frequency-response error. Both options are common in budget plugins. Neither is great.

Wave Digital Filters solve this differently, and a growing number of analog-modeled plugins lean on this framework, including various high-end tube and transformer plugins. (Note: other serious analog modeling approaches exist alongside WDF. Cytomic uses optimized Modified Nodal Analysis, u-he Diva uses Zero Delay Feedback filters, and both achieve excellent results through different math.) It is worth understanding even if you never write one, because it explains a lot about why some emulations behave like the original circuit and others only sound like it on a sine sweep.

The core trick: change variables

Alfred Fettweis published the original WDF paper in 1971, before plugin developers existed, while designing recursive digital filters for telecommunications. The idea is small but consequential.

Instead of describing each circuit node with voltage v and current i, describe it with two wave variables:

  • a = v + R·i (incident wave)
  • b = v - R·i (reflected wave)

R is a free parameter called the port resistance. You pick one for every port in the circuit. The transformation is invertible: given a and b you can recover v and i.

This looks like an arbitrary substitution. It is not. Three things become true the moment you switch to wave variables:

  1. A resistor with resistance equal to its port resistance reflects nothing. If you set R = R_resistor, then b = 0 regardless of a. The element is "matched."
  2. A capacitor becomes a one-sample delay. Apply the bilinear transform to a capacitor's impedance 1/(sC) with port resistance R = T/(2C), and the wave equation collapses to b[n] = a[n-1]. A pure z⁻¹. No coefficients, no multiplications.
  3. An inductor becomes a sign-flipped one-sample delay (b[n] = -a[n-1]) under the same trick.

The first time you derive this, it feels like cheating. Capacitors are usually the hard part of digital filter design - they are where the bilinear transform's frequency warping comes from. In WDF land, every capacitor in your circuit is just a delay line with a flipped port resistance.

Adaptors handle the wiring

That handles individual components. Connecting them is where the structure shows up. Kirchhoff's laws (voltages sum around a loop, currents sum at a node) translate into elements called series adaptors and parallel adaptors - small blocks of additions and multiplications that take incoming waves from each connected port and produce outgoing waves.

A three-port series adaptor, for example, computes coefficients from the three port resistances and uses them to redistribute waves so that the series-connection law (currents equal, voltages sum) holds. Same idea for parallel.

Crucially, at every adaptor port we are free to choose the port resistance. And there is one specific choice - the "reflection-free" port - that makes one of the outgoing waves depend only on the other incoming waves at that adaptor, not on the wave coming in at that port itself. This is the structural payoff: by carefully choosing port resistances, you can build a tree of adaptors and elements where signals always flow forward in time. No delay-free loops, no implicit equations.

The whole circuit becomes a directed tree. You evaluate it leaf-to-root and back, once per sample, and you are done. The arithmetic is roughly comparable to a direct-form IIR of the same order.

Why this matters for sound

Two properties fall out of WDF that direct discretization does not give you for free.

Passivity is preserved. A passive analog circuit cannot generate energy. The wave-digital structure inherits this property exactly: the discrete system is provably passive, sample by sample, regardless of input level or coefficient quantization. This translates to numerical stability that does not depend on the user staying inside a "safe" parameter range. A naive bilinear-transformed circuit can blow up at extreme settings; a properly built WDF cannot.

Nonlinear elements plug in cleanly. A diode, a triode, or a JFET model has a current-voltage characteristic i = f(v). In wave form, this becomes an implicit equation b = g(a) that, for many devices, you can solve in closed form (for diodes, the Lambert W function gives an analytic answer). For circuits with a single nonlinearity isolated at the root of the tree, the rest of the WDF runs explicitly and only the nonlinear root needs an iterative or table-based solve. That is dramatically cheaper than discretizing the whole circuit and Newton-iterating the entire state vector every sample.

This second property is why WDF became the framework of choice for serious tube and transistor emulations starting around the mid-2000s. Werner and others extended the original framework to handle multiple nonlinearities and more complex topologies (R-type adaptors, MNA-derived adaptors), which removed the last big limitation.

What you actually hear

The audible payoff of all this math shows up in three places.

Behavior at extreme drive. Bilinear-transformed nonlinear circuits often introduce small artifacts that grow with input level - tiny instabilities, frequency-warping inconsistencies, drift in the working point of the nonlinearity. WDF emulations stay clean because passivity is enforced.

Component interaction. In a real tube preamp, the cathode-bypass capacitor and the plate load resistor and the coupling cap all interact: pushing the tube harder shifts the bias point, which shifts the gain, which shifts the EQ. A WDF emulation captures this because it models the circuit topology, not just an input-output transfer function. Many cheaper "tube" plugins are static waveshapers with a fixed pre/post EQ - they cannot do this, and the difference is audible on transients.

Behavior outside the design point. Static models break when you do something the designer did not anticipate (extreme frequencies, weird envelopes, oversample-then-decimate paths). Topology-based models degrade gracefully. They behave like a circuit because they are a circuit, just running on samples instead of electrons.

When it is overkill

WDF is not always the right tool. For a clean, linear EQ, a parametric biquad is faster and indistinguishable. For a single static waveshaper, a precomputed lookup with antialiasing (BLEP-style or oversampled) is simpler and sounds the same. The cost-benefit only kicks in when you have interacting reactive elements and nonlinear elements together - which is exactly the territory of tube preamps, compressor sidechains, transformer saturation, and analog filters with nontrivial feedback paths.

If you have ever wondered why a serious analog modeling plugin from a small developer can outperform a much larger company's offering, the answer is often that the small developer chose WDF and spent six months on a single circuit, while the bigger team shipped a static waveshaper plus an EQ curve and called it done. The framework is freely available. The willingness to use it correctly is what separates the two.

Further reading

For the original theory: Fettweis, "Wave Digital Filters: Theory and Practice" (Proceedings of the IEEE, 1986). For modern nonlinear extensions: the work of Kurt James Werner and the WDF research at Stanford CCRMA. For practical implementation: the open-source chowdsp_wdf library demonstrates the full framework in JUCE/C++ and is short enough to read end-to-end.