Skip to content
Plugins (183)

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
Runs with the native Zolo host

Plugin snippets can be edited in the Playground, but native capabilities are executed locally with the Zolo CLI.

/ 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

From import to result

  1. 01Import only what you use
  2. 02Copy the smallest example
  3. 03Run it with the Zolo CLI

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]}")

Global index

Find your way through Zolo

Try an idea

Start here

9 results

9 results

en