Task 3 of 5

Taper the Edges

You cannot make an arbitrary signal fit the window: you rarely know its frequency in advance, and a real signal has many at once. What you can do is make it end where it starts. Multiply every sample by a taper that falls to zero at both edges, and the tiled copies join at zero with no cliff left for the transform to explain.

The Hann window is the workhorse: w[i] = 0.5 - 0.5·cos(2πi / n), a single raised cosine. One multiply per sample, no neighbours, no accumulation — this is the most trivially parallel kernel in the course, and on a GPU it is free next to the transform that follows it.

One honest footnote, because you will meet both spellings. The symmetric window divides by n - 1 and is genuinely zero at both ends; it is the right choice when the window is a filter's impulse response. The periodic (DFT-even) window divides by n, so its n samples are exactly one period of the cosine — which is what makes a windowed on-bin tone land on three bins and nothing else. The two differ by under one part in a hundred and the argument is decades old, but for spectral analysis the answer is not in doubt: divide by n.

And nothing is free. The taper throws most of the signal away near the edges, so the measured peak comes back smaller — worse than before, until task 5 gives it back — and it widens the main lobe: one sharp bin becomes three. A window buys dynamic range and pays for it in resolution. The next task puts numbers on that trade.

Goal: write the Hann window kernel, then measure the worst leak with no window and with Hann on the same 8.5-cycle tone.

Requirements

Hint 1 — the kernel body

Math.PI and Math.cos both work inside kernels, and this.constants.n is already wired up:

const i = this.thread.x;
const w = 0.5 - 0.5 * Math.cos(2 * Math.PI * i / this.constants.n);
return signal[i] * w;
Hint 2 — and then use it

The taper goes on the samples, so it has to run before the transform sees them:

const tapered = measure(magnitude(spectrum(hann(offBin))));

If the number you get back is the rectangular one again, the windowed signal never reached spectrum.

Hint 3 — what you should see

The leak drops from about -18 dB to about -47 dB: the worst stray bin goes from an eighth of the peak to a four-hundredth of it. And the Hann peak amplitude comes back around 0.42further from the true 1.00 than the unwindowed 0.65 was. That is not a mistake; it is the window's coherent gain, and task 5 divides it out.

Same idea elsewhere

Every real analyser windows: an AnalyserNode in the Web Audio API applies a Blackman window before its FFT and does not offer you the choice, numpy ships hanning/hamming/blackman as one-liners, and MATLAB's pwelch defaults to Hamming. On a GPU the taper is a per-element multiply you fuse into whatever pass already touches the samples — a thrust::transform in CUDA, two lines at the top of the load in WGSL.

All tasks in Windowing & Spectral Leakage

  1. A Tone the Window Does Not Fit
  2. What the Transform Actually Sees
  3. Taper the Edges
  4. Measure the Trade
  5. Give the Amplitude Back

This page is an interactive exercise — the editor, the GPU runner and your saved progress need JavaScript. The text above is the full brief.