Reaction–diffusion

반응-확산 패턴

The spots and stripes that emerge when two chemicals react and diffuse together. Alan Turing proposed the model in 1952 to explain natural patterns like leopard spots and zebra stripes.

Also known as: Gray–Scott modelTuring pattern
···
js
const c = document.createElement('canvas');
document.body.appendChild(c);
c.style.width = '100%'; c.style.height = '100%';
const ctx = c.getContext('2d');
const cs = getComputedStyle(document.documentElement);
const rgb = (cs.getPropertyValue('--accent-3').trim() || '#18c29c').match(/[0-9a-f]{2}/gi).map((x) => parseInt(x, 16));
const GW = 110, GH = 66;
const small = document.createElement('canvas');
small.width = GW; small.height = GH;
const sctx = small.getContext('2d');
const img = sctx.createImageData(GW, GH);

let U = new Float32Array(GW * GH).fill(1);
let V = new Float32Array(GW * GH).fill(0);
for (let k = 0; k < 6; k++) {
  const cx = (0.2 + Math.random() * 0.6) * GW, cy = (0.2 + Math.random() * 0.6) * GH;
  for (let y = 0; y < GH; y++) for (let x = 0; x < GW; x++) {
    if ((x - cx) ** 2 + (y - cy) ** 2 < 16) { U[y * GW + x] = 0.5; V[y * GW + x] = 0.25; }
  }
}
const FEED = 0.055, KILL = 0.062, DU = 1, DV = 0.5;
function idx(x, y) { return ((y + GH) % GH) * GW + ((x + GW) % GW); }
// Karl Sims 튜토리얼의 가중 3x3 라플라시안 — 4점 라플라시안보다 완만해서 dt=1 에서도 안정적이다
function lap(F, x, y) {
  return F[idx(x - 1, y - 1)] * 0.05 + F[idx(x, y - 1)] * 0.2 + F[idx(x + 1, y - 1)] * 0.05
    + F[idx(x - 1, y)] * 0.2 - F[idx(x, y)] + F[idx(x + 1, y)] * 0.2
    + F[idx(x - 1, y + 1)] * 0.05 + F[idx(x, y + 1)] * 0.2 + F[idx(x + 1, y + 1)] * 0.05;
}
function step() {
  const U2 = new Float32Array(U), V2 = new Float32Array(V);
  for (let y = 0; y < GH; y++) for (let x = 0; x < GW; x++) {
    const i = y * GW + x;
    const u = U[i], v = V[i], uvv = u * v * v;
    U2[i] = u + (DU * lap(U, x, y) - uvv + FEED * (1 - u));
    V2[i] = v + (DV * lap(V, x, y) + uvv - (FEED + KILL) * v);
  }
  U = U2; V = V2;
}
function render() {
  for (let i = 0; i < GW * GH; i++) {
    const n = Math.max(0, Math.min(1, V[i] * 2.4));
    const j = i * 4;
    img.data[j] = 13 + n * (rgb[0] - 13); img.data[j + 1] = 13 + n * (rgb[1] - 13); img.data[j + 2] = 18 + n * (rgb[2] - 18); img.data[j + 3] = 255;
  }
  sctx.putImageData(img, 0, 0);
  ctx.imageSmoothingEnabled = true;
  ctx.drawImage(small, 0, 0, c.width, c.height);
}
function resize() {
  const dpr = Math.min(devicePixelRatio || 1, 2);
  c.width = innerWidth * dpr; c.height = innerHeight * dpr;
}
addEventListener('resize', resize);
resize();
for (let i = 0; i < 3000; i++) step(); // 스크린샷 시점에 이미 무늬가 자라 있도록 미리 계산
render();
(function loop() {
  for (let i = 0; i < 6; i++) step();
  render();
  requestAnimationFrame(loop);
})();

Give every cell in a grid two concentrations, U and V. U is continually replenished (fed) and V is continually removed (killed); wherever the two meet, V consumes U and converts it into more V. Add diffusion — each cell's chemicals spreading into its neighbours via a Laplacian — and this handful of rules alone produces startlingly organic spots, coral and maze-like patterns. This demo uses one specific variant, the Gray–Scott model.

The pattern is extremely sensitive to just two numbers, feed and kill: nudge them slightly and the result swings between dots, stripes, mazes and spirals. It's standard practice to simulate on a small grid (say 120×70) and draw it scaled up — a finer grid gives denser patterns at the cost of more computation per step.

Use it for coral- or cell-like organic textures, background patterns for logos and posters, and procedural texture generation in general. Seed the initial state with a few random blobs and it grows a different shape every time.

When to use

Use it for organic background textures with a coral or cellular feel. Exposing feed/kill as live parameters speeds up exploring the pattern space.