Computed live From the course lessons Illustrative defaults

Every curve and every number on this page is computed, in this browser, by real complex arithmetic. H(ejω) is evaluated on the actual unit circle as g·∏(z − zk) / ∏(z − pk); the impulse response h[n] is obtained by running the difference equation forward from n = 0, which is why it really does diverge when you drag a pole outside the circle; the group delay is a genuine derivative of the unwrapped phase. Nothing here is a plausible-looking curve that was drawn rather than calculated.

From the course lessons, not invented here: the notation (H(z) = g ∏(z−zk)/∏(z−pk), ω in rad/sample, a pole at r ejθ, τ(ω) = −d∠H/dω) is m4-l3.html; the difference equation y[n] = Σbkx[n−k] − Σaky[n−k] and the BIBO test Σ|h[n]| < ∞ are m3-l4.html; the exterior ROC |z| > |a| is m4-l2.html; and the worked notch (60 Hz at fs = 8 kHz, so ω0 = 2π×60/8000 = 0.0471 rad/sample, which is 0.0150π) is m4-l3.html:412. It is recomputed here rather than quoted — and stated in both units, because 0.0471 is the value in radians per sample and 0.0150π is the same number expressed as a fraction of π. Those two are the pair to quote; "0.0471π" would be π times too large.

What is illustrative: the starting radii and angles of each preset (r = 0.85, 0.90, 0.95; θ = 0.25π, …) are chosen so the effect is legible on a small plot — they are not measurements of any real filter. The heat map's dB compression and its ±40 dB clip are display choices. Move any of them and all four views recompute exactly.

The z-plane, and the four views of one filter

Drag a pole (×) or a zero (○) and watch the magnitude response, the phase, the group delay and the impulse response all move at once. The single idea this page exists to make visible: the frequency response is a slice through the surface |H(z)| taken along the unit circle. A pole near the circle pushes that slice up into a peak; a zero on the circle pulls it down to a true null. Poles and zeros are told apart by shape, not colour. Keyboard: tab to any marker, then arrow keys to move it — or type r and θ into the table.

Every thumbnail above is live — drawn from the same arithmetic as the full-size plots, so it moves the instant you move a root. Hover or tab to a block and it marks the plot it produces and the controls that change it. Block 3 is the one to stare at: |H(z)| is a surface over the whole plane, and the response is its height along the unit circle. Nothing else on this page is as worth understanding.

1 · Placement — where the poles and zeros go

Drag a pole toward the unit circle and the magnitude peak gets taller and narrower, and h[n] rings for longer. Cross the circle and h[n] blows up — the filter is unstable. Drag a zero onto the circle and the response hits exactly zero at that angle.

Turning the conjugate-pair lock off lets a complex root stand alone: the coefficients then go complex and h[n] grows an imaginary part, so no real filter can be built. Values are shown as a radius (a plain ratio — 1.00 is the unit circle) and an angle in degrees and as a fraction of π rad/sample.

2 · H(z) — the gain in front of the polynomials
× (dimensionless)

g moves the whole magnitude curve up or down and changes nothing else — not the peak positions, not the phase, not the stability. Normalising to unity DC gain divides g by |H(1)|, which is the usual way to make a lowpass pass a constant through unchanged. It is undefined for a filter with a null at DC, and the verdict panel says so instead of dividing by nearly zero.

3 · The slice — evaluating H on the unit circle
0.000π ×π rad/sample Hz

The cursor is a point walking the unit circle, and the dashed lines are the distances that produce |H| there — numerator distances from the zeros divided by denominator distances from the poles. Watch the cursor pass close to a pole and the magnitude plot rise at the same instant.

fs only relabels the axis in Hz: the filter is defined in ω, radians per sample, and ω = π is always fs/2. Changing fs from 8 kHz to 48 kHz moves no pole and no curve.

1 · The z-plane — |z| = 1 is the unit circle

Poles ×, zeros ○, the unit circle, and (optionally) the |H(z)| surface underneath as a heat map. The ω cursor is the small square walking the circle.

× pole ○ zero □ ω cursor on the unit circle · faint marker = conjugate partner (moves with its handle) |z| = 1 the unit circle — the line the stability rule is about

Numeric entry — the same control, by typing

A full equivalent to dragging. Radius r and angle θ (in units of π) for every root. With the conjugate lock on, a root with 0 < θ < π stands for a pair at ±θ.

kind r θ / π θ (deg) as remove

2 · Magnitude |H(ejω)|

computed

3 · Phase ∠H and group delay τ(ω)

computed

4 · Impulse response h[n]

computed

What each view is telling you

Conventions, stated rather than assumed.
Where this sits in the course. It is the lab declared at courses/dsp101/SYLLABUS.md:169-170 — "Interactive pole-zero placement on z-plane / Real-time frequency response update / Drag poles/zeros and see filter shape change / Stability indicator". Lesson 4.3 makes the geometric argument in prose: evaluating |H| at ω "amounts to measuring the distance from each pole and zero to the point ejω". Lesson 4.4 gives the BIBO criterion and the marginal case. Lesson 3.4 gives the difference equation this page runs to get h[n]. Nothing here extends the syllabus; it draws what those three lessons already say.

The arithmetic, in full