All projects

ECG noise-filtering animation

Finished 2026

A five-minute animated explainer, made in Manim, of why 60 Hz mains hum wrecks an ECG and what a brick-wall low-pass and a moving average each do about it.

Three stacked traces of a heartbeat signal, noisy at the top, ringing in the middle and clean and smooth at the bottom

The idea

An ECG is a tiny signal sitting next to a very loud wall socket. I wrote an animated walkthrough in Manim of how that noise shows up and two ways to deal with it. The video is about 296 seconds and has six scenes: a cold open, where the noise comes from, finding it in the frequency domain, a naive filter, a moving average, and a side-by-side comparison.

The signal

There’s no real recording in this one. Each heartbeat is a sum of five Gaussians for the P, Q, R, S and T waves, at 72 beats per minute sampled at 500 Hz for 5 seconds. On top of that go two kinds of noise: 60 Hz mains hum at 0.25 amplitude and broadband noise at 0.08 from a seeded random generator.

The two filters

The frequency-domain scene puts the trace over its FFT out to 120 Hz, with the heart at the low end, a spike at exactly 60 Hz, and a flat floor of everything else.

The noise has a fingerprint in the spectrum

The naive filter is a brick wall: take the FFT, zero everything above 20 Hz and transform back. It kills the noise and it also rings around the sharp QRS spike, which I circled in red. I used 20 Hz because at 40 Hz the ringing is there mathematically but too small to see.

Ringing around the spike

The moving average is an 8-point window (np.convolve with a box). It has no ringing. I worked out that at a 500 Hz sample rate an 8-point average has its first null at 62.5 Hz, which takes the 60 Hz hum down to about 4% of its size. It also rounds off the QRS spike a little. The filter scenes drop the broadband noise on purpose so the ringing is the only thing to watch, so the final comparison is hum only.

The moving average window
A 26 second clip from the video: the moving average window sweeping across the noisy trace.