# Paint the Pattern

*Task 4 of 4 · [Reaction–Diffusion](https://gpu.rocks/learn/reaction-diffusion-bc3d0b34.md) · GPU.js Learn*

Payoff time. The whole simulation is wired below — a 64×64 grid and a
**steps** dial saying how long to run it — 200 is the value that
turn the seed square into coral — and it ends holding `v`, a grid of numbers
with coral growing in it.
Numbers deserve pixels: one graphical kernel, exactly like the painters in 3.1,
turns the V field into the picture the module cover promised.

The palette is fixed so we can test it: brightness
`t = min(1, v·2.5)`, painted as
`color(t, t·t, 0.25 + 0.75·t)` — a deep-blue ocean at `v = 0`
rising through violet to white-hot at the pattern's crest.

## Goal

**Goal:** complete the `paint` kernel — map this thread's
`v` value through the palette and put it on screen.

## Requirements

- Read this thread's value: `v[this.thread.y][this.thread.x]`
- Brightness `t = Math.min(1, value * 2.5)`
- Paint `this.color(t, t * t, 0.25 + 0.75 * t, 1)`

## Hint 1 — same move as the luminance painter

This is the paint kernel from **Pixels from Scratch** with a fancier
ramp: read one number
from the grid, compute the channels, call `this.color()`.
`Math.min` works inside kernels.

## Hint 2 — the whole body

```js
const t = Math.min(1, v[this.thread.y][this.thread.x] * 2.5);
this.color(t, t * t, 0.25 + 0.75 * t, 1);
```

## Same idea elsewhere

Compute passes that end in a draw are the shape of every GPU simulation you've
seen on the web: WebGPU chains compute pipelines into a render pipeline whose fragment
shader is your `paint`; Metal apps do the same with a compute encoder feeding
a fragment function. The data never has to leave the card.

## Starter code

```js
// Gray–Scott on a dial, then paint the V field. The sim is done —
// the painter is yours.
const gpu = new GPU({ mode });

const stepU = gpu.createKernel(function (u, v) {
  const x = this.thread.x;
  const y = this.thread.y;
  let xl = x - 1; if (xl < 0) xl = this.constants.size - 1;
  let xr = x + 1; if (xr > this.constants.size - 1) xr = 0;
  let yd = y - 1; if (yd < 0) yd = this.constants.size - 1;
  let yu = y + 1; if (yu > this.constants.size - 1) yu = 0;
  const uc = u[y][x];
  const vc = v[y][x];
  const lap = u[y][xl] + u[y][xr] + u[yd][x] + u[yu][x] - 4 * uc;
  return uc + (this.constants.du * lap - uc * vc * vc
    + this.constants.f * (1 - uc)) * this.constants.dt;
}, { output: [64, 64], constants: { size: 64, du: 0.2, f: 0.035, dt: 1 } });

const stepV = gpu.createKernel(function (u, v) {
  const x = this.thread.x;
  const y = this.thread.y;
  let xl = x - 1; if (xl < 0) xl = this.constants.size - 1;
  let xr = x + 1; if (xr > this.constants.size - 1) xr = 0;
  let yd = y - 1; if (yd < 0) yd = this.constants.size - 1;
  let yu = y + 1; if (yu > this.constants.size - 1) yu = 0;
  const uc = u[y][x];
  const vc = v[y][x];
  const lap = v[y][xl] + v[y][xr] + v[yd][x] + v[yu][x] - 4 * vc;
  return vc + (this.constants.dv * lap + uc * vc * vc
    - (this.constants.f + this.constants.k) * vc) * this.constants.dt;
}, { output: [64, 64], constants: { size: 64, dv: 0.1, f: 0.035, k: 0.06, dt: 1 } });

const paint = gpu.createKernel(function (v) {
  // TODO: t = Math.min(1, value * 2.5), then
  // this.color(t, t * t, 0.25 + 0.75 * t, 1)
  this.color(1, 0, 1, 1);
}, { output: [64, 64], graphical: true });

// A dial, not a constant: slider() re-runs the whole program when you drag it,
// so this is how far the chemistry gets — 0 is the bare seed square, 1 is the
// 200 steps that grow it into coral, 3 keeps going until the branches crowd
// each other.
const steps = slider('steps', { min: 0, max: 600, value: 200, step: 10 });

let u = seedU;
let v = seedV;
for (let i = 0; i < steps; i++) {
  const nextU = await stepU(u, v);
  const nextV = await stepV(u, v);
  u = nextU;
  v = nextV;
}

await paint(v);
render(paint.canvas);
```

---

Interactive version: https://gpu.rocks/learn/reaction-diffusion-bc3d0b34/4

[Previous task](https://gpu.rocks/learn/reaction-diffusion-bc3d0b34/3.md)
