Skip to content

terrain

stable

A heightmap-based terrain generation and manipulation plugin supporting procedural generation (diamond-square), hydraulic erosion, noise, normals, and grayscale export.

use plugin terrain::{create_heightmap, get_height, set_height, …}
16 functions Graphics
/ filter jk navigate Esc clear
Functions (16)
  1. create_heightmap Create an empty heightmap of given size
  2. get_height Read height at integer coordinates
  3. set_height Write height at integer coordinates
  4. normalize Scale all heights to the [0, 1] range
  5. smooth Average-blur the heightmap N times
  6. diamond_square Generate terrain via diamond-square algorithm
  7. normal_at Surface normal vector at a point
  8. slope_at Slope angle in radians at a point
  9. sample_bilinear Interpolated height at fractional coordinates
  10. erosion_hydraulic Simulate particle-based hydraulic erosion
  11. flatten_area Blend a circular area toward a target height
  12. add_noise Add value noise with amplitude and frequency
  13. min_max Get minimum and maximum height values
  14. scale Multiply all heights by a factor
  15. clamp Clamp all heights to a min/max range
  16. to_grayscale_bytes Export heightmap as grayscale byte array

Overview

terrain is a heightmap toolkit for procedurally generating and reshaping 2D elevation grids. The core value is a plain heightmap table with width, height, and a flat data array of row-major float heights — there are no opaque handles, so a heightmap is just a table you can pass around, inspect, and serialize. Every transform takes a heightmap and returns a new heightmap, which makes operations easy to chain. Use it whenever you need procedural landscapes (diamond-square), realistic weathering (hydraulic erosion), detail (value noise), or analysis and export (normals, slopes, grayscale bytes).

The mental model is a pipeline: start from create_heightmap or diamond_square, layer detail with add_noise, weather it with erosion_hydraulic and smooth, normalize or clamp the range, then sample it with get_height / sample_bilinear or hand it off with to_grayscale_bytes.

Common patterns

Generate a landscape, weather it, and normalize the result:

use plugin terrain::{terrain}

let raw = terrain.diamond_square(129, 0.9, 42)
let eroded = terrain.erosion_hydraulic(raw, 4, 800, 7)
let map = terrain.normalize(terrain.smooth(eroded, 1))
print("ready: {map["width"]}x{map["height"]}")

Build flat ground, add layered noise, then carve out a build site:

use plugin terrain::{terrain}

let base = terrain.create_heightmap(128, 128)
let hills = terrain.add_noise(base, 1.0, 0.04, 1)
let detail = terrain.add_noise(hills, 0.3, 0.12, 2)
let plot = terrain.flatten_area(terrain.normalize(detail), 64, 64, 12.0, 0.5)

Sample interpolated heights and surface normals for lighting:

use plugin terrain::{terrain}

let map = terrain.normalize(terrain.diamond_square(65, 0.8, 99))
let h = terrain.sample_bilinear(map, 32.5, 32.5)
let n = terrain.normal_at(map, 32, 32)
print("height {h}, normal.z {n["z"]}")

Create an empty heightmap of given size

Creates a new heightmap table with all values set to 0. The table has fields width, height, and data. This is the starting point for all terrain operations.

use plugin terrain::{terrain}

let map = terrain.create_heightmap(256, 256)
print("created {map["width"]}x{map["height"]} map")

Read height at integer coordinates

Returns the height value at integer grid position (x, y). Returns 0.0 for out-of-bounds coordinates.

use plugin terrain::{terrain}

let map = terrain.create_heightmap(64, 64)
let h = terrain.get_height(map, 10, 20)
print("height at (10,20): {h}")

Write height at integer coordinates

Sets the height at grid position (x, y) and returns the updated heightmap. Out-of-bounds writes are silently ignored.

use plugin terrain::{terrain}

let map = terrain.create_heightmap(64, 64)
let map2 = terrain.set_height(map, 32, 32, 1.0)

Scale all heights to the [0, 1] range

Rescales all height values to [0, 1] based on the current minimum and maximum. Returns a new heightmap.

use plugin terrain::{terrain}

let map = terrain.diamond_square(65, 0.7, 42)
let normalized = terrain.normalize(map)

Average-blur the heightmap N times

Applies a 3x3 averaging blur for iterations passes. Each pass smooths sharp features into gentler slopes.

use plugin terrain::{terrain}

let map = terrain.diamond_square(65, 1.5, 99)
let smooth = terrain.smooth(map, 3)

Generate terrain via diamond-square algorithm

Generates a procedural heightmap using the diamond-square (midpoint displacement) algorithm. size is rounded up to the nearest 2^n+1. Higher roughness produces more jagged terrain.

use plugin terrain::{terrain}

let map = terrain.diamond_square(129, 0.8, 12345)
let map2 = terrain.normalize(map)
print("{map2["width"]}x{map2["height"]} terrain generated")
use plugin terrain::{terrain}

let rough = terrain.diamond_square(65, 1.5, 1)
let smooth_terrain = terrain.smooth(terrain.normalize(rough), 2)

Surface normal vector at a point

Returns the surface normal vector at (x, y) as a table with fields x, y, z. The vector is normalized. Useful for lighting calculations.

use plugin terrain::{terrain}

let map = terrain.diamond_square(65, 0.7, 42)
let n = terrain.normal_at(map, 32, 32)
print("normal: ({n["x"]}, {n["y"]}, {n["z"]})")

Slope angle in radians at a point

Returns the slope angle in radians at (x, y), computed from central differences. Flat areas return 0, steep cliffs return values near π/2.

use plugin terrain::{terrain}

let map = terrain.diamond_square(65, 0.8, 7)
let angle = terrain.slope_at(map, 20, 20)
print("slope: {angle} radians")

Interpolated height at fractional coordinates

Returns a smoothly interpolated height at fractional coordinates (fx, fy) using bilinear interpolation. Useful when sampling terrain at sub-pixel precision.

use plugin terrain::{terrain}

let map = terrain.diamond_square(65, 0.7, 1)
let h = terrain.sample_bilinear(map, 10.5, 20.75)
print("interpolated height: {h}")

Simulate particle-based hydraulic erosion

Simulates hydraulic erosion by dropping water particles that erode high points and deposit sediment in valleys. More iterations and drops produce more realistic results.

use plugin terrain::{terrain}

let map = terrain.diamond_square(65, 0.9, 42)
let eroded = terrain.erosion_hydraulic(map, 3, 500, 99)
let result = terrain.normalize(eroded)

A light single pass keeps mountains intact while still cutting gullies; smoothing afterward softens the eroded channels:

use plugin terrain::{terrain}

let map = terrain.diamond_square(129, 1.2, 5)
let carved = terrain.erosion_hydraulic(map, 1, 200, 1)
let final = terrain.smooth(carved, 1)

Blend a circular area toward a target height

Smoothly blends a circular area centered at (cx, cy) toward target_height. The blend uses a linear falloff so edges stay natural. Useful for placing buildings or roads.

use plugin terrain::{terrain}

let map = terrain.diamond_square(65, 0.7, 5)
let flat = terrain.flatten_area(map, 32, 32, 10.0, 0.5)

Add value noise with amplitude and frequency

Adds value noise to the heightmap. amplitude controls the height of noise features, frequency controls their density (higher = more detail).

use plugin terrain::{terrain}

let map = terrain.create_heightmap(128, 128)
let noisy = terrain.add_noise(map, 0.5, 0.05, 777)
let noisy2 = terrain.add_noise(noisy, 0.25, 0.1, 888)
let result = terrain.normalize(noisy2)

Get minimum and maximum height values

Returns {min, max} — the minimum and maximum height values in the entire heightmap. Useful before normalizing or clamping.

use plugin terrain::{terrain}

let map = terrain.diamond_square(65, 0.8, 1)
let bounds = terrain.min_max(map)
print("range: {bounds["min"]} to {bounds["max"]}")

Multiply all heights by a factor

Multiplies all height values by factor. Use to change vertical scale before compositing multiple heightmaps.

use plugin terrain::{terrain}

let map = terrain.diamond_square(65, 0.7, 1)
let tall = terrain.scale(map, 2.0)

Clamp all heights to a min/max range

Clamps all height values to the [min, max] range. Values below min become min and values above max become max.

use plugin terrain::{terrain}

let map = terrain.diamond_square(65, 1.2, 42)
let clamped = terrain.clamp(map, -1.0, 1.0)

Clamp the floor to zero to create flat sea level, then normalize so the remaining land fills the full range:

use plugin terrain::{terrain}

let map = terrain.diamond_square(65, 0.8, 3)
let coastline = terrain.normalize(terrain.clamp(map, 0.0, 1.0))

Export heightmap as grayscale byte array

Exports the heightmap as a flat byte buffer where each byte (0–255) represents one pixel's brightness. The heights are normalized automatically. The byte order is row-major (left to right, top to bottom).

use plugin terrain::{terrain}

let map = terrain.normalize(terrain.diamond_square(65, 0.7, 42))
let pixels = terrain.to_grayscale_bytes(map)
print("exported {map["width"] * map["height"]} bytes")

Because export normalizes internally, you can dump a raw generated map directly — the brightest pixel is the highest peak regardless of the source range:

use plugin terrain::{terrain}

let map = terrain.diamond_square(129, 1.0, 2026)
let pixels = terrain.to_grayscale_bytes(map)
print("first pixel: {pixels[0]}")
enespt-br