Task 3 of 5
Shorten the window and the time axis sharpens. Lengthen it and the frequency axis
does. You cannot have both, and this is not a gpu.js limitation or a numerical one — it is
a theorem. A window w samples long cannot tell two frequencies apart unless they
differ by roughly 2 · SR / w, and it cannot place an event in time to better
than w samples. Multiply those two together and the window length cancels: the
product is a constant, and picking a window is choosing which end of it to spend.
This signal has both kinds of detail. Two steady tones, 512 Hz and 640 Hz, run the whole way through; and at 0.254 s there is a single sample driven hard — a click, the sharpest event a sampled signal can hold. Build the same spectrogram twice: same hop, same 64 frames, one with a 64-sample window and one with a 256-sample window. Then measure both, on both axes. The two measurements are already written; do not take anyone's word for which window wins, because neither of them does.
One kernel source, two compilations. this.constants.win is fixed
when a kernel is created — which is precisely why it is legal as a loop bound — so handing
createKernel a different constants object turns the same function body into a
different kernel. Same trick a CUDA template parameter plays.
shortSpec uses win: 64 and keeps 32 binslongSpec uses win: 256 and keeps 128 bins32) and the same 64 frames, so their time axes line upA window of w real samples has w/2 bins below Nyquist, each
SR / w hertz wide. A 64-sample window gives 32 bins 64 Hz apart; a
256-sample window gives 128 bins 16 Hz apart.
const shortSpec = makeSpectrogram(64, 32);
const longSpec = makeSpectrogram(256, 128);
makeSpectrogram is plain JavaScript — win and bins reach
the kernel through its settings object, never through a closure, which is why this compiles at
all.
The short window finds one peak where there are two tones, and pins the click to 2 frames. The long window resolves two peaks and smears the same click across 6. Neither is the right answer; they are the same information spent differently.
template<int WIN> in CUDA, override constants in WGSL,
function constants in Metal. The specialisation is what lets the compiler unroll the loop.
This page is an interactive exercise — the editor, the GPU runner and your saved progress need JavaScript. The text above is the full brief.