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.
Math Formula
Noise at (x,y) - built from stacked octaves.
i = octave number (controls detail level).
Each octave doubles frequency (2^i) and halves amplitude (1/(2^i)).
Base noise function provides raw values.
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)