Perlin Terrain
About This Sketch
Perlin noise creates a flowing, organic terrain that evolves smoothly over time. Unlike pure randomness, Perlin noise has spatial coherenceโnearby points have similar values, creating natural-looking landscapes.
This technique revolutionized computer graphics and is used everywhere from video game terrain to procedural texture generation. The flowing motion comes from treating time as a third dimension in the noise space.
Algorithm
Rolling terrain created with Perlin noise, a natural-looking smooth random function invented by Ken Perlin. The landscape flows and undulates organically.
**Key Concepts:**
- **Perlin Noise**: Coherent noise function that creates smooth, natural randomness
- **3D Noise**: Using time as third dimension for smooth animation
- **Terrain Generation**: Classic application of noise functions
- **Continuous Motion**: Time parameter creates flowing landscape
**How it works:**
1. Draw multiple horizontal "scan lines" across the canvas
2. For each point, sample Perlin noise at (x, y, time)
3. Map noise value (0-1) to vertical offset (-30 to 30)
4. Increment time each frame for smooth evolution
5. Gradient colors from top to bottom suggest depth
6. Result: terrain that appears to flow continuously
Pseudocode
SETUP:
time = 0
DRAW (every frame):
CLEAR background
FOR y = 50 to canvas_height STEP 15:
BEGIN curve
FOR x = 0 to canvas_width STEP 5:
noise_value = perlin_noise(x ร 0.01, y ร 0.01, time)
vertical_offset = map(noise_value, 0, 1, -30, 30)
depth = map(y, 50, canvas_height, 0, 1)
color = interpolate(accent1, accent2, depth)
ADD vertex at (x, y + vertical_offset)
END curve
time = time + 0.01
Source Code
let sketch = function(p) {
let time = 0;
p.setup = function() {
p.createCanvas(400, 300);
p.colorMode(p.RGB);
};
p.draw = function() {
const colors = getThemeColors();
p.background(...colors.bg);
p.noFill();
for (let y = 50; y < p.height; y += 15) {
p.beginShape();
for (let x = 0; x < p.width; x += 5) {
let noiseVal = p.noise(x * 0.01, y * 0.01, time);
let wave = p.map(noiseVal, 0, 1, -30, 30);
let t = p.map(y, 50, p.height, 0, 1);
let r = p.lerp(colors.accent1[0], colors.accent2[0], t);
let g = p.lerp(colors.accent1[1], colors.accent2[1], t);
let b = p.lerp(colors.accent1[2], colors.accent2[2], t);
p.stroke(r, g, b);
p.strokeWeight(2);
p.vertex(x, y + wave);
}
p.endShape();
}
time += 0.003;
};
};