webdevpuneet.com

Canvas Noise Terrain Generator — Free Perlin FBM Heightmap Snippet

Canvas Noise Terrain Generator · Animations · Plain HTML, CSS & JS · Live preview

CategoryAnimations
TagsCanvas

Copy & paste

Source Code

<div class="nt-wrap">
  <canvas id="ntCanvas" class="nt-canvas"></canvas>
  <div class="nt-panel">
    <span class="nt-tag">perlin noise · procedural</span>
    <div class="nt-row">
      <label>Scale <span id="ntScaleVal">0.008</span></label>
      <input type="range" id="ntScale" min="2" max="20" value="8" step="1" />
    </div>
    <div class="nt-row">
      <label>Octaves <span id="ntOctVal">4</span></label>
      <input type="range" id="ntOct" min="1" max="6" value="4" step="1" />
    </div>
    <button class="nt-btn" id="ntRegen">Regenerate</button>
  </div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

From-scratch Perlin noise
A full gradient-noise implementation, no library.
Fractal Brownian motion
Multiple octaves combine broad shape with fine detail.
Elevation color banding
Seven realistic terrain bands with interpolated boundaries.
Live scale/octave controls
Sliders re-render the terrain in real time.
Regenerate button
Reseeds the permutation table for a brand-new landscape.
ImageData rasterization
Direct pixel-buffer rendering for full control.
Reduced-resolution sampling
A performance-conscious block-fill strategy.
DPR-aware rendering
Crisp terrain on high-density displays.

About this UI Snippet

Canvas Noise Terrain Generator — Perlin Noise and Elevation Banding

Screenshot of the Canvas Noise Terrain Generator snippet rendered live

This snippet generates a topographic-map-style terrain from nothing but math — a from-scratch Perlin-style gradient noise implementation, stacked into fractal Brownian motion, colored by elevation band, and rasterized directly into ImageData.

Gradient noise, not value noise

noise2D() implements classic Perlin noise: a shuffled permutation table (seedNoise()) assigns each integer lattice point a pseudo-random gradient direction, and the noise value at any point is a smoothed (fade()-eased) interpolation between the dot products of the surrounding lattice gradients and the offset vectors to them. This produces smooth, organic-looking randomness — unlike naive per-pixel random values, which look like static rather than terrain.

Fractal Brownian Motion builds detail at multiple scales

A single noise octave produces smooth, blobby shapes with no fine detail. fbm() samples the same noise function multiple times at doubling frequency and halving amplitude, summing the results — broad, low-frequency octaves establish sweeping hills and valleys, while higher-frequency, lower-amplitude octaves layer in coastline roughness and small-scale variation on top. This is exactly the technique real terrain-generation and cloud-rendering systems use to avoid the "obviously smooth math function" look.

Elevation bands, not a raw gradient

Rather than mapping height directly to a smooth color gradient, colorFor() steps through a fixed sequence of elevation thresholds (deep water, shallow water, beach, grassland, forest, rock, snow) and linearly interpolates color only *within* each band. That banding, plus interpolation at the boundaries, is what makes the noise field read as a legible topographic map rather than an abstract color blob.

Rasterizing at reduced resolution

Sampling the noise function at every device pixel would be unnecessarily slow for a real-time regenerate button. Instead the field is sampled every few CSS pixels and each sample fills a small block in the pixel buffer — a resolution/performance trade-off similar to the one in canvas metaball blobs.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA colored terrain heightmap renders immediately.
  2. 2
    Adjust ScaleLower values zoom into broader, smoother terrain features.
  3. 3
    Adjust OctavesMore octaves add finer, rougher detail on top of the base shape.
  4. 4
    Click RegenerateA new random permutation table produces an entirely new landscape.
  5. 5
    Read the elevation bandsDeep water, shallow water, beach, grass, forest, rock, and snow.
  6. 6
    Resize the windowThe terrain regenerates to fill the new canvas size.

Real-world uses

Common Use Cases

Worldbuilding and game tools
Provide a compact reference for procedural heightmaps, with a from-scratch gradient noise implementation and no library.
Generative art backgrounds
Create a unique topographic-style hero, with fractal Brownian motion combining broad shapes and fine detail across multiple octaves.
Map and geo product pages
Reinforce a mapping product's identity with a contour-like visual, using seven elevation bands with interpolated boundaries.
Noise algorithm lessons
Teach Perlin noise and fractal Brownian motion visually, with live scale and octave sliders re-rendering the terrain instantly.
Procedural generation portfolios
Demonstrate from-scratch generation on canvas ImageData, giving a technical audience something they can adjust and explore.
Related: Canvas Water Ripple Simulation
See the Canvas Water Ripple Simulation for a related animations pattern worth pairing with this one.
Related: SVG Mask Sweep Text Reveal
See the SVG Mask Sweep Text Reveal for a related animations pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

Simple per-pixel random values produce visual static with no spatial coherence between neighboring pixels. Perlin (gradient) noise instead assigns pseudo-random gradient directions to a lattice of integer points and smoothly interpolates between them, so nearby points produce similar, correlated values — that spatial smoothness is what makes it look like organic terrain rather than TV static.

A single noise octave produces smooth, rounded, blobby shapes with no fine texture at any scale. fbm() sums several octaves at doubling frequency and halving amplitude, so low-frequency octaves set the broad hills and valleys while higher-frequency octaves add progressively finer roughness on top — the combination is what gives the terrain both large-scale structure and small-scale detail simultaneously.

Regenerate calls seedNoise(), which reshuffles the 256-entry permutation table using Math.random(). Since every noise lookup depends on that table to determine gradient directions at each lattice point, a freshly shuffled table produces an entirely different noise field and therefore an entirely different terrain layout, even though the fbm and coloring logic are unchanged.

colorFor() intentionally steps through a fixed list of elevation bands (water, beach, grass, forest, rock, snow) rather than mapping height to a single smooth color ramp. Interpolating color only within each band, and switching bands at fixed height thresholds, is what produces the legible, map-like banding instead of an abstract, unreadable color blend.

Yes — sample the noise field with an offset that increases each frame (e.g. fbm((x + t) * scale, y * scale, octaves) with t incrementing slowly) and call generate() inside a requestAnimationFrame loop instead of only on slider/button events. Because noise2D is a pure function of its input coordinates, shifting those coordinates over time produces a smoothly scrolling terrain with no other changes needed.