n.← All worldsHOW IT IS MADE

A GUIDE

How a coordinate becomes a world.

The demos open on the seed k2v-1 k2em — a nod to k·2^v − 1, the law that puts a ridge on every quadtree edge; type any words to get another world. Both demos are built from one small library, nedgelaw, which packages Lars Rocha's RangeBlends mathematics as a dependency-free toolkit with the same API in Python and JavaScript. This page explains the idea, shows the calls, walks through how each demo uses them, and ends with how to write a terrain law of your own.

The idea: ask, don't store

Most generated worlds are simulated: you run a process over a grid, and the result is a map you have to keep. Everything here is indexed instead. A tile, a house, a tree is a pure function of a seed and a coordinate. Nothing is stored, so nothing has to be loaded; the same call gives the same answer on any machine, in either language, without anyone having generated the tiles around it.

ned.World("k2v-1 k2em").tile(511, 127)
# {'x': 511, 'y': 127, 'height': 0.7288…, 'biome': 'rock', 'walkable': True,
#  'tree': False, 'village': {'min': 7, 'em': '🏡', 'n': 'cottage'}, 'name': 'Gevasu'}

That cottage at Gevasu exists because v2(512) and v2(128) — the number of trailing zeros in each coordinate plus one — are 9 and 7, and the lesser, 7, which the settlement ladder reads as cottage, and the rocky ground there is walkable. No one placed it. Zoom in and the same rule tells you where its walls stand; land and you can walk around it. A deeper zoom is a longer index, not a bigger download.

Locality is the rule. Every law in this library answers for one coordinate without looking at its neighbours. That is what makes an Earth-sized planet possible: level 20 of Planet One has a trillion cells, and any one of them costs the same to ask for.

The primitives

RangeBlends supplies a handful of indexed functions; everything else is composition. The same names exist on ned in both languages (Python uses snake_case).

FunctionWhat it isWhere it shows up
at(seed, x, y, stream), unit(h)The indexed hash: a 32-bit value for any integer coordinate and stream, and its 0…1 form.Every decision — is there a tree, which way does a river bend.
streamOf(name)A stream id from a name. Streams keep unrelated decisions independent.Every recipe names its streams, with its version in the name.
v2(n)The ruler sequence: trailing zeros of n. Divisible numbers rank high.Village rank, tributary order, and — on the planet — mountain ridges.
CUM, runAt(g)Cumulative run lengths from the measured edge law (docs/EDGE_LAW.md).Ridge amplitude by scale; the original world's north–south ranges.
fbm1, fbm2, value2Value noise and its octave sums, all built on at.Continents, wander, presence, the block-scale detail you walk on.
squareRamp(k, v), buildRidgeThe massif slide and the ridge profile: peaks at k·2^v − 1, half-width WIDTH, valleys deepened by RELIEF.The ranges recipe, in 2-D on the atlas and on the cube-sphere.
network(seed, order, meander), RiverA drainage basin walked from its mouth, tributaries chosen by the ruler.The library demo's river; the pattern behind the planet's channels.

Quick start

PYTHON 3.10+

python -m pip install -e .          # from the repository root
import nedgelaw as ned

world = ned.World("k2v-1 k2em")
world.tile(511, 127)                # one tile, from nothing

ranges = ned.World("k2v-1 k2em", edge_mode="ranges",
                   edge_strength=1, extent=(1024, 512))
ranges.height(511, 127)             # the same address, another law

ground = ned.Landscape("k2v-1 k2em")     # 16 blocks to a tile
ground.column(383 * 16 + 5, 127 * 16 + 5)
ground.props(511, 127)              # the cottage's boxes

planet = ned.Planet("k2v-1 k2em", recipe="ranges")
planet.sample(50.17, 154.04)        # elevation, biome, address, settlement
planet.settlement(50.17, 154.04)    # the houses and their plan
planet.trees("+Y", 15, 22405, 28587)

JAVASCRIPT (NODE OR BROWSER)

const ned = require("./js");        // or import from "./js/index.mjs"
                                    // in the browser: <script src="js/nedgelaw.js">
const world = new ned.World("k2v-1 k2em");
world.tile(511, 127);

const ranges = new ned.World("k2v-1 k2em",
  {edgeMode: "ranges", edgeStrength: 1, extent: [1024, 512]});
ranges.height(511, 127);

const ground = new ned.Landscape("k2v-1 k2em");
ground.column(383 * 16 + 5, 127 * 16 + 5);
ground.props(511, 127);

const planet = new ned.Planet("k2v-1 k2em", {recipe: "ranges"});
planet.sample(50.17, 154.04);
planet.settlement(50.17, 154.04);
planet.trees("+Y", 15, 22405, 28587);

The two implementations are held to agree: the test suite generates reference values with Node and asserts Python reproduces them — to twelve decimal places for the atlas laws, within a micrometre for the planet — including every house in a settlement and every tree in a patch.

How the Atlas works

  1. The world is a window. World.height(x, y) is evaluated for a 1024×512 (or 512×256) window of tiles and wrapped onto a sphere. The renderer ray-marches real relief at Landscape's own scale — 64 blocks over a radius of 16384 / 2π blocks — so ridges cast shadows and break the horizon.
  2. Zoom generates. Past about 2.6× the atlas builds a detail window for the piece of world on screen, sampled with the same bilinear anchors and detail octaves as Landscape.heightAt, snapped to a tile lattice so panning reuses it, never finer than one block. Trees and houses are painted from Landscape.props with a block-height test, so the roof you see from orbit is the roof the walker bumps into.
  3. Landing is the same arithmetic. The ground view meshes Landscape.chunk columns and props; the NEARBY map is the globe's own detail patch painted on a 2-D canvas.
  4. Recipes are versions. Original is untouched. Ranges & rivers is buildRidge's law read in two dimensions and bent by a per-tile warp, with segmented ranges, cross-spurs, basins that flood, and indexed channels — a trunk per 64 tiles, tributaries on ruler rows. Compare Original here flips the law at one coordinate; that is the whole engine in one click.

How Planet One works

  1. Addresses. Six cube faces, each a quadtree to level 24. A patch is (face, level, column, row); Planet.patch returns its heights, Planet.height(direction) any single point. Nothing above a patch is ever materialised; two workers stream what the camera can see.
  2. The mountains sit on the address. A face at level 20 is a chart of 2²⁰ cells (~9.5 m). A ridge of scale v stands where the cell index is k·2^v − 1 — exactly where the quadtree has an edge at level 20 − v. The ruler sequence and the quadtree are the same counting, so a deeper zoom is more bits of the same number and the law never needs a new octave table.
  3. Rivers, villages, woods, by the same rule. Trunks live in every 4,096-cell valley and are alive only where their own signal says so, giving each a source and a mouth; tributaries leave at hashed angles on ruler rows. Settlements sit on level-13 cells where min(v2(column+1), v2(row+1)) ≥ 3 and the ground agrees — World.villageAt on the sphere. Trees fill level-17 cells by a forest field and their own hash.
  4. Seams. Near a face edge every face within 8% of dominant is read and blended; the shared edge samples are bitwise equal. Colour, sky and fog are the renderer's; the law is only heights and props.

Write your own law

A terrain recipe is a class with one method. For the atlas it receives the original height and returns a new one; for the planet it receives a unit direction and returns metres. Four rules keep it honest:

JAVASCRIPT · js/hills.js

class HillsTerrain {
  constructor(seed, strength = 0.65) {
    this.key = streamOf(seed); this.strength = strength;
    this.warp = streamOf("hills-v1-warp");
    this.crest = streamOf("hills-v1-crest");
  }
  apply(x, y, original) {
    const bend = (fbm2(this.key, x * 0.01, y * 0.01, 3, this.warp) - 0.5) * 40;
    const per = 64, k = Math.floor((x + bend + 1) / per + 0.5);
    const distance = Math.abs(x + bend - (k * per - 1)) / (per * 0.5 * WIDTH);
    const ridge = distance < 1 ? smooth(1 - distance) * (CUM[6] / CUM[VMAX]) : 0;
    const height = Math.min(1, original + 0.5 * ridge);
    return original + this.strength * (height - original);
  }
}

PYTHON · src/nedgelaw/hills.py

class HillsTerrain:
    def __init__(self, seed, strength=0.65):
        self.key = stream_of(seed); self.strength = strength
        self.warp = stream_of("hills-v1-warp")
        self.crest = stream_of("hills-v1-crest")

    def apply(self, x, y, original):
        bend = (fbm2(self.key, x * 0.01, y * 0.01, 3, self.warp) - 0.5) * 40
        per = 64
        k = math.floor((x + bend + 1) / per + 0.5)   # Math.round
        distance = abs(x + bend - (k * per - 1)) / (per * 0.5 * WIDTH)
        ridge = smooth(1 - distance) * (CUM[6] / CUM[VMAX]) if distance < 1 else 0
        height = min(1, original + 0.5 * ridge)
        return original + self.strength * (height - original)

Then register the mode name in World (JavaScript: js/api.js; Python: src/nedgelaw/world.py), add it to the build in tools/build_js.py, add an option to the Atlas's recipe selector, and copy the parity test pattern from tests/test_extension.py: Node writes JSON, Python asserts equality. js/ranges.js and src/nedgelaw/ranges.py are the full worked example; js/planet.js and src/nedgelaw/planet.py show the same law on a sphere.

Limits, honestly

Further reading