Recursive Filters

Probability
Sampling Methods
Kalman Filter
Moving Average
Exponential Moving Average
Recursive Filters
Author

Apurva Nakade

Published

August 7, 2026

Each of these filters updates a running estimate from only the previous estimate and the newest measurement \(z_i\) — no stored history of the stream — which is what makes them run in O(1) memory and O(1) compute per sample. The simple moving average and exponential moving average (a.k.a. a first-order low-pass filter) both trade responsiveness for smoothness through a hand-picked constant (\(k\) or \(\alpha\)) that never changes:

\[ \text{SMA: } \hat x_i = \frac{1}{k}\sum_{j=i-k+1}^{i} z_j, \qquad \text{EMA: } \hat x_i = \alpha\, \hat x_{i-1} + (1-\alpha)\, z_i. \]

The Kalman filter instead maintains a running estimate of its own uncertainty \(P_i\) and derives its gain from it at every step, given only two beliefs about the noise: \(R\), the assumed measurement-noise (sensor) variance, and \(Q\), the assumed process-noise variance (how much the true value is expected to drift between samples):

\[ P_i^- = P_{i-1} + Q,\qquad K_i = \frac{P_i^-}{P_i^- + R},\qquad \hat x_i = \hat x_{i-1} + K_i\,(z_i - \hat x_{i-1}),\qquad P_i = (1-K_i)\,P_i^-, \]

with no \(\alpha\) or \(k\) to tune by hand — open the convergence plots on the constant-signal example below to watch \(K_i\) shrink automatically as the filter grows more confident.

Examples

The examples below walk through the trade-offs each filter makes: smoothing versus lag, a fixed hand-tuned weight versus an adaptive one, and what happens when the Kalman filter’s own noise assumptions (\(R\), \(Q\)) don’t match reality.

Click any example to load it into the app.

Noisy sine wave
f(x) = sin(x),   σ = 0.4
The default comparison: all three filters recover a smooth sine from noisy measurements.
Estimating a hidden constant
f(x) = 5,   σ = 1
The classic Kalman use case: repeated noisy measurements of a fixed unknown value. Open the convergence plots and watch the Kalman gain shrink toward 0 as it grows more confident.
Sudden regime change
f(x) = 6 for x > 10, else 0
A sudden jump exposes each filter's lag. Heavier smoothing (larger k or higher α) takes longer to catch up; raising Kalman's Q lets it re-adapt faster after the jump.
Fast oscillation exposes lag
f(x) = sin(3x),   σ = 0.3
A quickly changing signal shows heavily smoothed filters trailing noticeably behind the peaks. Try lowering α or k, or raising Kalman's Q, to track it more closely.
Heavy noise, gentle signal
f(x) = cos(x/2),   σ = 0.8
Noise larger than the signal itself needs aggressive smoothing (high α, large k) just to recover a usable trend.
Tracking a steady drift
f(x) = 0.3x,   σ = 1
A steadily drifting quantity. Kalman's Q > 0 lets the filter keep tracking the drift instead of falling permanently behind, the way Q = 0 would.
Overconfident sensor model
f(x) = sin(x),   σ = 0.6 but R = 0.01
Setting R far below the actual measurement noise makes Kalman overtrust every sample — its estimate ends up almost as jagged as the raw data, showing why R must reflect real sensor noise.
Zero process noise stops tracking
f(x) = sin(x),   Q = 0
With Q = 0 the filter assumes the true value never changes, so its gain decays toward 0 and it stops following the moving sine — compare with the default example, where Q = 0.05 keeps it responsive.

References

Staszewski, Kuba. “Recursive Filters.” Blog post. https://www.staszewski.xyz/blog/recursive-filters/