# Slide a Window: 1D Convolution

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

A **convolution** slides a small window of weights along a signal:
each output sample is a weighted average of the input around it. With weights
`[0.25, 0.5, 0.25]` the window *smooths* — every sample leans toward
its neighbors and jitter cancels out.

On the GPU nothing actually slides. Every output sample gets its own thread, and each
thread reads its *own* three inputs, all at the same time. The only wrinkle is the
ends: sample 0 has no left neighbor, so we **clamp** — reuse the nearest
in-bounds sample instead of reading past the edge.

## Figures

- **nothing slides — thread x just reads its own three samples**

## Goal

**Goal:** smooth the 128-sample `signal` — each output is
`0.25·left + 0.5·center + 0.25·right`, with indexes clamped at both ends.

## Requirements

- Read this thread's neighbors: `signal[x - 1]` and `signal[x + 1]`
- Clamp the indexes — below `0` becomes `0`, above `127` becomes `127`
- Return `0.25·left + 0.5·center + 0.25·right`

## Hint 1 — nothing slides

Thread `x` only ever touches `signal[x - 1]`,
`signal[x]` and `signal[x + 1]`. Three reads, one weighted sum,
done — the "sliding" is 128 threads doing this at once.

## Hint 2 — clamping with an if

```js
let left = x - 1;
if (left < 0) left = 0;
```

and the mirror image
for `right` against `127`. Plain `if` statements work
fine inside kernels.

## Hint 3 — the whole body

```js
let left = x - 1;
if (left < 0) left = 0;
let right = x + 1;
if (right > 127) right = 127;
return 0.25 * signal[left] + 0.5 * signal[x] + 0.25 * signal[right];
```

## Same idea elsewhere

Neighborhood reads like this are called *stencil* patterns in CUDA and
ROCm — the classic optimization is staging the window in shared memory. A WebGPU compute
shader does the same thing with neighboring buffer reads inside a workgroup.

## Starter code

```js
// Convolution: each output sample is a weighted average of its neighborhood.
const gpu = new GPU({ mode });

const smooth = gpu.createKernel(function (signal) {
  const x = this.thread.x;
  // TODO: return 0.25 * left + 0.5 * center + 0.25 * right,
  // clamping the neighbor indexes so x = 0 and x = 127 stay in bounds.
  return signal[x];
}, { output: [128] });

const result = await smooth(signal);
console.log('before:', signal[63], ' after:', result[63]);
```

---

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

[Next task](https://gpu.rocks/learn/convolution-and-filters-66933805/2.md)
