Perlin noise

펄린 노이즈

A function that produces natural, cloud-like continuous randomness. Devised by Ken Perlin in 1983 for the film Tron.

Also known as: Gradient noiseProcedural noise
···
js
const c = document.createElement('canvas');
document.body.appendChild(c);
c.style.width = '100%'; c.style.height = '100%';
const ctx = c.getContext('2d');
const small = document.createElement('canvas');
const sctx = small.getContext('2d');
const SW = 96, SH = 60;
small.width = SW; small.height = SH;
const img = sctx.createImageData(SW, SH);

// 클래식 그래디언트(펄린) 노이즈: 격자점마다 랜덤 방향 벡터를 두고 내적을 보간
const GRID = 16;
const grads = [];
for (let i = 0; i < GRID * GRID; i++) { const a = Math.random() * Math.PI * 2; grads.push([Math.cos(a), Math.sin(a)]); }
function grad(ix, iy) { return grads[((iy % GRID + GRID) % GRID) * GRID + ((ix % GRID + GRID) % GRID)]; }
function fade(t) { return t * t * t * (t * (t * 6 - 15) + 10); }
function lerp(a, b, t) { return a + (b - a) * t; }
function perlin(x, y) {
  const x0 = Math.floor(x), y0 = Math.floor(y), xf = x - x0, yf = y - y0;
  const g00 = grad(x0, y0), g10 = grad(x0 + 1, y0), g01 = grad(x0, y0 + 1), g11 = grad(x0 + 1, y0 + 1);
  const d00 = g00[0] * xf + g00[1] * yf, d10 = g10[0] * (xf - 1) + g10[1] * yf;
  const d01 = g01[0] * xf + g01[1] * (yf - 1), d11 = g11[0] * (xf - 1) + g11[1] * (yf - 1);
  const u = fade(xf), v = fade(yf);
  return lerp(lerp(d00, d10, u), lerp(d01, d11, u), v);
}

const cs = getComputedStyle(document.documentElement);
const rgb = (cs.getPropertyValue('--accent').trim() || '#5b5bf7').match(/[0-9a-f]{2}/gi).map(x => parseInt(x, 16));
function render(t) {
  for (let y = 0; y < SH; y++) for (let x = 0; x < SW; x++) {
    const n = (perlin(x * 0.12 + t * 0.6, y * 0.12) + 1) / 2;
    const i = (y * SW + x) * 4;
    img.data[i] = rgb[0] * n; img.data[i + 1] = rgb[1] * n; img.data[i + 2] = rgb[2] * n; img.data[i + 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();
render(0); // 첫 프레임부터 바로 보이게
let t = 0;
(function loop() { t += 0.02; render(t); requestAnimationFrame(loop); })();

Plain Math.random() has no relationship between neighbouring values, so plotting it just gives static. Perlin noise instead assigns a gradient (direction) to every point on a grid, and any point inside a cell blends the surrounding gradients smoothly — nearby samples end up close in value, distant ones diverge, which is what makes the noise feel continuous rather than random.

This demo treats time as a third axis — sampling the same 2D noise at a slowly shifting offset each frame — to get a drifting, cloud-like animation. Computing it at low resolution and scaling up keeps the cost small.

It shows up anywhere motion needs to feel neither rigid nor chaotic: terrain and cloud textures, camera shake, a character's idle sway. Perlin's own 2002 refinement, simplex noise, is faster in higher dimensions and has fewer grid-aligned artifacts.

When to use

Use it whenever you need "natural irregularity" — camera shake, idle animation, generated textures.