Task 2 of 5

What the Transform Actually Sees

Why should half a cycle wreck a spectrum? Because every basis function the DFT measures against completes a whole number of cycles in the window. What it can represent exactly, then, is a signal that repeats with period n — so that is what it assumes you gave it. Hand it 256 samples and it does not see a 256-sample excerpt of something longer. It sees those 256 samples tiled end to end, forever.

Tile a tone with 8 whole cycles and the copies join invisibly: sample 255 runs into sample 0 exactly as if the cosine had never stopped. Tile one with 8.5 and every join is a cliff — the wave ends near the bottom and restarts at the top, half a cycle skipped. A cliff is broadband: no small set of smooth sinusoids can build a step, so the transform pays for it with a little energy in every bin it has. That is where the smear in the last task came from. It was never in your signal; the transform put it there, faithfully describing a discontinuity you never intended.

You can measure that cliff without transforming anything, and one kernel does it: the step from each sample to the next in the repeated signal. The wrap is the whole point — sample 255's neighbour is sample 0 of the next copy.

half a cycle left over, and every join is a cliff the transform has to describe
Goal: write the wrapped-difference kernel and log, for both tones, the size of the step at the seam and the largest step anywhere in the window.

Requirements

Hint 1 — the wrap

% works inside a kernel (it transpiles to GLSL's mod()), so the neighbour of sample i in the repeated signal is signal[(i + 1) % 256]. At i = 255 that is signal[0] — the seam.

Hint 2 — the kernel body
const i = this.thread.x;
return signal[(i + 1) % this.constants.n] - signal[i];
Hint 3 — what you should see

For the tone that fits, the seam step is the smallest kind of step in the window — about a tenth of the biggest. For the one that does not, the seam step is the biggest step, ten times anything the wave does inside the window. That factor of a hundred between the two seams is the leakage, before you have transformed anything at all.

Same idea elsewhere

Circular boundaries are the default in frequency-domain work everywhere: cufftExecR2C, WGSL FFT compute passes and every convolution-theorem implementation treat the buffer as a ring. It is the same wraparound the Cellular Automata module uses for a toroidal grid — and exactly why frequency-domain convolution has to be zero-padded before use, or the tail of the filter wraps round and lands on the beginning of the signal.

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.