WTF is perlin noise?

Back

April 9, 2025

Perlin noise = structured randomness. No hard jumps, just smooth, flowing variation.

Instead of random values at points, it assigns random gradients to a grid. Noise emerges from interpolation.

Fade function (smoothstep-like) ensures transitions aren't janky.

More octaves = richer detail. Stack 'em at different frequencies/amplitudes for better texture.

Used in: procedural textures, terrain gen, cloud sims, motion effects.

Key Takeaway: Perlin noise isn't chaos; it's controlled, organic variation.

Algorithm

  • Generate a grid of pseudo-random gradient vectors.
  • Compute dot products between gradients and distance vectors.
  • Smooth with a fade function.
  • Interpolate to get final noise value.
const randomGradient = () => {
  const angle = Math.random() * Math.PI * 2;
  return [Math.cos(angle), Math.sin(angle)];
};

const dotGridGradient = (gradients, ix, iy, x, y) => {
  if (!gradients[`${ix},${iy}`]) {
    gradients[`${ix},${iy}`] = randomGradient();
  }
  const [gx, gy] = gradients[`${ix},${iy}`];
  return (x - ix) * gx + (y - iy) * gy;
};

const fade = (t) => t * t * t * (t * (t * 6 - 15) + 10);
const lerp = (a, b, t) => a + t * (b - a);

const perlinNoise = (x, y, gradients = {}) => {
  const x0 = Math.floor(x),
    x1 = x0 + 1;
  const y0 = Math.floor(y),
    y1 = y0 + 1;

  const sx = fade(x - x0);
  const sy = fade(y - y0);

  const n0 = dotGridGradient(gradients, x0, y0, x, y);
  const n1 = dotGridGradient(gradients, x1, y0, x, y);
  const ix0 = lerp(n0, n1, sx);

  const n2 = dotGridGradient(gradients, x0, y1, x, y);
  const n3 = dotGridGradient(gradients, x1, y1, x, y);
  const ix1 = lerp(n2, n3, sx);

  return lerp(ix0, ix1, sy);
};

console.log(perlinNoise(1.3, 2.1));
// Output perlin at (1.3, 2.1)