# Any Filter, One Kernel

*Task 2 of 5 · [Convolution & Filters](https://gpu.rocks/learn/convolution-and-filters-66933805.md) · GPU.js Learn*

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

**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

- Loop `for (let i = 0; i < this.constants.size; i++)` — a constant is a legal bound
- Tap index: `x + i - this.constants.radius`, clamped to `0…127`
- Accumulate `filter[i] * signal[tap]` into `sum` and return it

## 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

```js
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.

## Starter code

```js
// One kernel, any 5-tap filter: weights come in as data, size as constants.
const gpu = new GPU({ mode });

const convolve = gpu.createKernel(function (signal, filter) {
  const x = this.thread.x;
  let sum = 0;
  // TODO: loop i from 0 to this.constants.size,
  //   tap index = x + i - this.constants.radius (clamped to 0…127),
  //   accumulate filter[i] * signal[tap].
  return sum;
}, {
  output: [128],
  constants: { size: 5, radius: 2 },
});

const gauss = [0.06, 0.24, 0.4, 0.24, 0.06];
const result = await convolve(signal, gauss);
console.log('smoothed sample 64:', result[64]);
```

---

Interactive version: https://gpu.rocks/learn/convolution-and-filters-66933805/2

[Previous task](https://gpu.rocks/learn/convolution-and-filters-66933805/1.md) · [Next task](https://gpu.rocks/learn/convolution-and-filters-66933805/3.md)
