Perlin Noise: The Algorithm That Made Procedural Generation Look Good
A deep dive into the Perlin noise algorithm, from the fundamentals of coherent noise to the implementation details of gradient vectors and permutation tables.
Procedural generation is nothing without noise, and the most famous noise function is Perlin noise. This article walks through the algorithm step by step, starting from the basic concept of a noise function to the full implementation.
What is Noise?
At its core, noise is just a collection of random values — a function that takes N parameters and returns a value according to some rules. For 2D noise, you can visualize it as a grayscale texture: each pixel's brightness corresponds to the noise value at that coordinate.
The naive approach — using a simple random function like rand(x + y) — produces white noise: every pixel is independent, with no discernible pattern. That's not very useful for generating terrain, clouds, or textures.
Coherent Noise
What you actually want is coherent noise, which has three properties:
- Same input always returns the same output
- A small change in input produces a small change in output
- A large change in input produces a random change in output
This gives you smooth, organic-looking patterns that still feel random overall.
Perlin's Algorithm
Ken Perlin developed this algorithm in 1983 while working on Disney's Tron, frustrated by the machine-like look of CGI. He won an Academy Award for it in 1997.
The core idea: for any point on a 2D plane, you find the four integer corners of the square it sits in. Each corner has a precomputed random unit vector (a gradient). You compute the vectors from each corner to the point, take the dot product of each pair, then interpolate the four dot products.
Gradients and Permutation Table
Since you can't precompute infinite gradients, Perlin uses a fixed set of 256 random unit vectors and a 256-element permutation table. The permutation table is shuffled using a Fisher-Yates-style swap, then duplicated to 512 entries to avoid buffer overflows.
Here's the setup code:
for (int i = 0; i < B; i++) {
permutation[i] = i;
gradients[i] = Vector2.Random();
gradients[i] = Vector2.Normalize(gradients[i]);
}
for (int i = 0, j = 0; i < B; i++) {
int k = permutation[i];
permutation[i] = permutation[j = random() % B];
permutation[j] = k;
}
for (i = 0; i < B + 2; i++) {
permutation[B + i] = permutation[i];
gradients[B + i] = gradients[i];
}Where B is 256. The permutation table is what makes the noise deterministic yet seemingly random — the same integer coordinate always maps to the same gradient, but adjacent coordinates get different gradients.
Computing the Noise Value
For a given point (x, y), you compute the relative vectors to the four corners and look up the gradients via the permutation table. Then you take dot products and interpolate using a smoothstep function (typically 6t⁵ - 15t⁴ + 10t³) to avoid visible grid artifacts.
The original implementation uses bitwise AND with 255 to wrap coordinates — effectively a modulo operation that keeps the noise periodic every 256 units. This is a practical limitation: beyond that range, the pattern repeats.
Ken Perlin developed Perlin noise in 1983 as a result of his frustration with the 'machine-like' look of computer-generated imagery at the time.