Task 2 of 5

Any Filter, One Kernel

Hardcoded weights mean writing a new kernel for every filter. The fix: pass the filter in as an ordinary array argument and loop over its taps. But a GPU loop wants bounds it can see at compile time — and that is exactly what this.constants is for: values baked into the kernel when it compiles, perfectly legal as loop bounds.

This kernel is built with constants: { size: 5, radius: 2 }. Tap i of the filter lines up with input sample x + i - radius — clamp that index like before and accumulate filter[i] * signal[tap].

Goal: finish the generic convolution — loop over this.constants.size taps, clamp each tap index, and return the accumulated weighted sum. One kernel, any 5-tap filter.

Requirements

Hint 1 — why constants?

Kernel arguments change per call; constants are frozen into the compiled kernel. That is why this.constants.size can bound a loop when a plain argument could not.

Hint 2 — the loop body
let tap = x + i - this.constants.radius;
if (tap < 0) tap = 0;
if (tap > 127) tap = 127;
sum += filter[i] * signal[tap];

Same idea elsewhere

Baked-in constants are a first-class idea everywhere: WGSL has pipeline-overridable constants, CUDA kernels take template parameters and __constant__ memory, Metal has function constants — all so the compiler knows your loop bounds and can unroll the filter loop.

All tasks in Convolution & Filters

  1. Slide a Window: 1D Convolution
  2. Any Filter, One Kernel
  3. Box Blur: the Window Goes 2D
  4. Sharpen: Negative Weights
  5. Sobel Edge Detection

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