Why not just call Math.random()?
Mountains, clouds, marble veins and wood grain all look random, but never chaotic — nearby points on a terrain map have similar heights. Math.random() gives you the opposite: every call is independent of the last, which produces static, not scenery. What you need is spatial coherence — a function where nearby inputs produce nearby outputs, but the overall pattern still looks organic rather than tiled. Ken Perlin invented exactly that while working on visual effects for Tron in 1982, and it remains the default tool for procedural content four decades later.
Gradient noise, not value noise
The naive approach — value noise — assigns a random number to each integer grid point and interpolates between them. It works, but produces a blobby look with visible creases at grid boundaries, because interpolating raw values doesn’t guarantee a smooth derivative. Perlin's fix was to assign each grid corner a random gradient vector instead of a scalar. The noise value at any point is the dot product of the nearest corner's gradient with the vector from that corner to the query point — corners whose gradient points toward you contribute positively, corners pointing away contribute negatively.
A fixed 256-entry permutation table, doubled to 512 entries to avoid index wrapping, supplies the "randomness": hash(x, y) = P[P[x & 255] + (y & 255)]. Because the table is fixed rather than freshly randomised, the same coordinates always produce the same noise value — essential for reproducible terrain and for evaluating noise independently on the CPU and GPU.
The fade curve that kills the creases
Linear interpolation between the four corner dot products still leaves a visible seam, because its derivative is discontinuous at grid lines. Perlin's 2002 revision uses a quintic fade curve instead of the original cubic one:
function fade(t) { return t*t*t*(t*(t*6 - 15) + 10); } // 6t⁵ − 15t⁴ + 10t³ function lerp(t, a, b) { return a + t * (b - a); } Both f(0)=0 and f(1)=1, but the quintic curve also has zero second derivative at both ends (C² continuity), which is what removes the fine "line artefacts" that show up when the noise is used to compute normal maps. Bilinearly blending the four gradient dot products with fade(x) and fade(y) gives a single octave of smooth, natural-looking noise — but the output range is roughly ±0.707 in 2D, not ±1 as often assumed.
function fade(t) { return t*t*t*(t*(t*6 - 15) + 10); } // 6t⁵ − 15t⁴ + 10t³
function lerp(t, a, b) { return a + t * (b - a); }
Fractional Brownian motion: layering octaves
One octave of noise gives gently rolling hills; real terrain has roughness at every scale simultaneously. Fractional Brownian motion (fBm) sums several octaves, each at double the frequency and half the amplitude of the last:
function fbm(x, y, octaves=6, lacunarity=2.0, gain=0.5) { let value = 0, amplitude = 0.5, frequency = 1.0, max = 0; for (let i = 0; i Octave 1 contributes continent-scale hills, octave 2 adds mountain ranges, octave 3 carves cliffs and escarpments, and octave 4 onward adds fine boulders and pebbles — each layer visually subordinate to the last, exactly matching how real landscapes look at every zoom level. Two useful variants: turbulence sums |noise| instead of noise for cloud-like bolts and marble veins, and ridged multifractal uses 1 − |noise| per octave for sharp mountain ridges. Domain warping — using one fBm field to distort the input coordinates of another, a technique popularised by Iñigo Quilez — produces the twisted, erosion-like patterns seen in high-end procedural terrain.
function fbm(x, y, octaves=6, lacunarity=2.0, gain=0.5) {
let value = 0, amplitude = 0.5, frequency = 1.0, max = 0;
for (let i = 0; i < octaves; i++) {
value += amplitude * noise2D(x * frequency, y * frequency);
max += amplitude; amplitude *= gain; frequency *= lacunarity;
}
return value / max; // normalise back to roughly ±1
}
Perlin noise and fBm: How maths paints nature
Perlin's own 2001 revision, simplex noise, replaces the square grid with a simplex lattice — triangles in 2D, tetrahedra in 3D — cutting the corners evaluated per sample from 2ⁿ to n+1 (4→3 in 2D, 8→4 in 3D) and removing the faint axis-aligned artefacts visible at multiples of 90° in classic Perlin noise. Its patent expired in 2021, so both versions are now fully free to use.
On the GPU, the permutation table is usually replaced by an arithmetic hash so the whole thing runs in a fragment shader with zero texture lookups — the standard building block for animated cloud layers, procedural marble, and terrain heightmaps computed per-pixel in real time.
Frequently asked questions
Який фактичний діапазон виведення для 2D шуму Перліна?
Приблизно від −0.707 до +0.707, а не від −1 до +1, як часто припускають. Максимальні значення досягаються лише під кутом 45° через градієнтний вектор. Для нормалізації до 0…1 використовуйте v = noise2D(x, y) * 0.707 + 0.5, або просто виміряйте фактичний діапазон для ваших параметрів і перемасштабуйте.
Чому fBm використовує подвоєння частоти та зменшення амплітуди?
Це імітує статистику реальних ландшафтів і турбулентності, де велика масштабна структура несе більшу частину дисперсії, а кожне дрібніше рівняння сприяє пропорційно менше. Подвоєння частоти (лакунарність ≈ 2), при зменшенні амплітуди (коефіцієнт ≈ 0.5, також відомий як стійкість) на октаву додає великі гірські хребти, потім гори, потім скелі, а потім дрібну шорсткість — кожен шар візуально підпорядковується попередньому.
Чи повинен я використовувати класичний шум Перліна чи шум Сімплекс?
Обидва є загальнодоступними тепер, коли патент на Сімплекс закінчився у 2021 році. Шум Сімплекс оцінює менше кутів сітки (3 замість 4 у 2D, 4 замість 8 у 3D) і не має артефактів, орієнтованих вздовж осі, що робить його швидшим для шуму на піксельному рівні в шейдерах. Класичний шум Перліна простіший у правильній реалізації для попередньо обчислених висотних карт у JavaScript, тому він залишається більш поширеною версією, яка викладається.
Спробуйте наживо
Усе, що вище, працює прямо у вашому браузері — відкрийте the simulation і змінюйте параметри під час роботи. Нічого не встановлюється, нічого не завантажується на сервер, уся модель живе в одній вкладці.
▶ Відкрити симуляцію the simulation