Where ideas percolate and thoughts brew

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;
    };
};