Moving Average Filter Simulator¶
Specification¶
The full specification below is extracted from Chapter 6: Buttons, Photoresistors, and Analog Sensor Input.
Type: microsim
**sim-id:** moving-average-filter-simulator<br/>
**Library:** p5.js<br/>
**Status:** Specified
Learning objective: Students will *evaluate* (Bloom L5: Evaluate) how window size and sampling rate together affect a moving average filter's tradeoff between responsiveness and noise reduction.
Canvas: 700x480px, responsive -- recompute plot width from `windowWidth` in `windowResized()`.
Layout: a single scrolling strip-chart plot showing two overlaid traces against a shared time axis -- a thin, jittery gray line for "Raw Sensor Reading" and a thicker raspberry-red `#C2185B` line for "Filtered (Moving Average)." The raw trace is generated from a smooth underlying signal (a slow sine wave representing a real change, such as a hand slowly passing over a photoresistor) plus random noise added each sample. A live readout beneath the plot displays the current window size in both "readings" and the equivalent time span in milliseconds, computed from the sampling rate control.
Controls: a `createSlider()` labeled "Window Size (readings)" (range 1-30, default 5); a `createSlider()` labeled "Sampling Rate (samples/sec)" (range 1-50, default 10); a `createSlider()` labeled "Noise Level" (range 0-100, default 40) controlling the amplitude of random jitter added to the raw signal; a `createButton()` labeled "Reset Trace" that clears the scrolling history and restarts the underlying sine wave from zero.
Interaction: as Window Size increases, the filtered trace visibly smooths and its peak lags further behind the raw trace's peak, with a small arrow annotation appearing once lag exceeds roughly 300 ms labeled "Filter lag." As Noise Level increases with a small window size, the filtered trace visibly still jitters, demonstrating that a too-small window under-filters; setting Window Size to 1 makes the filtered trace exactly equal the raw trace, visually proving the boundary case. Hovering anywhere on the plot shows a tooltip with the raw and filtered values at that time.
Implementation: p5.js, new samples pushed onto a fixed-length history array at intervals controlled by the Sampling Rate slider (via `millis()` timing rather than `frameRate()`, so it stays correct regardless of the browser's actual frame rate). Moving average recomputed each new sample from the last `windowSize` raw values. Scrolling implemented by shifting all plotted x-positions left each frame and dropping points that scroll off the left edge.