---
title: World generation
description: Generate flat or seeded terrain with caves, ores, trees, and reusable structures.
---

`ChunkHelper` stores blocks; the world-generation helpers fill it. Block names resolve through the bound protocol version, and the implementation has no runtime dependencies.

```ts
import { helpers } from "mc-protocol";

const world = helpers.createNoiseWorld({ seed: "mc-protocol", radius: 3 });
const surfaceY = world.generator.heightAt(0, 0);
const spawnY = Math.max(surfaceY, world.generator.seaLevel) + 1;
await client.sendPacket("LevelChunkWithLight", world.getLevelChunkWithLight(0, 0));
```

The result is a normal `ChunkHelper`, so its block and packet methods still apply. Use `helpers.forVersion(protocol)` to bind generation and block resolution to another built-in version.

## Flat worlds

`createFlatWorld(layers, options?)` lists layers from `minY` upward and fills every chunk within `radius`:

```ts
const world = helpers.createFlatWorld([
  { block: "bedrock", height: 1 },
  { block: "stone", height: 60 },
  { block: "dirt", height: 3 },
  { block: "grass_block", height: 1 },
]);
```

A block is a name (with or without `minecraft:`) or a global state ID. `radius` defaults to `3`; `minY`, `height`, `biomeId`, and `hasSkyLight` are passed to `ChunkHelper`.

## Noise worlds

```ts
const boulder = helpers.createBoulderStructure();
const ruin = helpers.createRuinStructure();
const cabin = helpers.createCabinStructure();

const world = helpers.createNoiseWorld({
  seed: "mc-protocol",
  radius: 3,
  seaLevel: 63,
  baseHeight: 70,
  amplitude: 32,
  terrain: { ridgeStrength: 0.25, warpAmplitude: 24 },
  caves: true,
  ores: true,
  trees: { density: 0.02 },
  structures: [
    {
      id: "boulders",
      structure: boulder,
      spacing: 24,
      separation: 8,
      chance: 0.7,
      offsetY: -1,
      maxSlope: 4,
      minDistanceFromOrigin: 24,
    },
    {
      id: "ruins",
      structure: ruin,
      spacing: 48,
      separation: 12,
      chance: 0.5,
      maxSlope: 3,
      foundation: "cobblestone",
      minDistanceFromOrigin: 24,
    },
    {
      id: "cabins",
      structure: cabin,
      spacing: 80,
      separation: 16,
      chance: 0.35,
      maxSlope: 3,
      foundation: "cobblestone",
      minDistanceFromOrigin: 24,
    },
  ],
});
```

`seed` accepts a number or string. The same seed and options produce the same result, independently of chunk generation order, including templates that cross chunk boundaries. All newer features are opt-in: omitting `terrain`, `caves`, `ores`, and `structures` preserves the existing seeded output.

Core options:

| Option | Default | Meaning |
| --- | --- | --- |
| `radius` | `3` | Chunks on each side of the origin; `3` creates 7 × 7 chunks |
| `seaLevel` | `63` | Highest water-filled Y |
| `baseHeight` | `64` | Average surface Y |
| `amplitude` | `24` | Maximum displacement from `baseHeight` before customization |
| `scale` | `0.01` | Horizontal terrain frequency |
| `octaves` | `4` | Terrain noise layers |
| `surface`, `filler`, `stone` | grass, dirt, stone | Dry top, subsurface, and deep material |
| `fillerDepth` | `3` | Subsurface thickness |
| `bedrock` | `"bedrock"` | Bottom block; `null` disables it |
| `water` | `"water"` | Water material; `null` disables it |
| `sand` | `"sand"` | Beach/submerged surface material; `null` disables it |
| `beachHeight` | `1` | Dry blocks above sea level that still count as beach |
| `trees` | off | `{ density, log?, leaves? }`; omitted leaves are persistent oak. Custom names use vanilla defaults; pass a numeric state ID for custom persistent leaves |
| `minY`, `height`, `biomeId`, `hasSkyLight` | `ChunkHelper` defaults | World shape, one biome ID for the whole world, and skylight |

### Terrain shaping

`terrain` accepts:

- `ridgeStrength` (default `0`) blends ordinary and ridged noise from `0` to `1`.
- `warpAmplitude` (default `0`) displaces input coordinates horizontally; `warpScale` defaults to `0.005`.
- `terraceStep` quantizes heights into steps of that many blocks; `0` disables terraces.
- `lacunarity` (default `2`) and `persistence` (default `0.5`) control octave frequency and amplitude.
- `heightAt(x, z, height)` can replace the computed height. Keep it pure; its result is rounded and clipped to world bounds.

`generator.heightAt(x, z)` returns the final terrain surface before caves are carved. This is also what surface placement and slope checks use.

### Caves and ores

`caves: true` enables chambers and intersecting tunnel fields. Use an object to tune them:

| Cave option | Default | Meaning |
| --- | --- | --- |
| `minY`, `maxY` | world minimum + 1, world top | Inclusive carving bounds; bottom bedrock is always protected |
| `scale` | `0.035` | Horizontal frequency; smaller values make larger cave features |
| `verticalScale` | `1.5` | Vertical frequency multiplier; smaller values make taller chambers |
| `threshold` | `0.55` | Chamber threshold, from -1 to 1; higher values make fewer chambers; 1 disables chambers |
| `tunnelWidth` | `0.09` | Tunnel width in noise space, from 0 to 1; 0 disables tunnels |
| `surfaceDepth` | `5` | Preserve the top five terrain layers, including the surface; 0 allows entrances |
| `lavaLevel` | world minimum + 10 | Fill carved blocks at or below this Y; `null` leaves all carved blocks as air |
| `lava` | `"lava"` | Material below `lavaLevel` |

For example, `caves: { threshold: 1, tunnelWidth: 0.12, lavaLevel: null }` creates tunnels without chambers or lava. Carving is independent of surface water; there is no aquifer simulation. With `surfaceDepth: 0`, underwater entrances can leave air beneath a water column. Set `caves: false` or `ores: false` to disable either feature explicitly.

`ores: true` enables coal, iron, gold, and diamond deposits. For custom deposits, pass an array:

```ts
const generator = helpers.createNoiseGenerator({
  ores: [{
    id: "granite-pockets",
    block: "granite",
    minY: -32,
    maxY: 48,
    scale: 0.1,
    threshold: 0.7,
    replace: ["stone", "deepslate"],
  }],
});
```

Each ore needs a unique stable `id`. `minY`/`maxY` default to world bounds, `scale` to `0.12`, and `threshold` to `0.65` (higher means rarer/smaller deposits). `replace` filters original terrain states and defaults to the configured stone. Bedrock at the world bottom is protected and carved blocks are never refilled. If multiple deposits match one block, lexical-last `id` wins. The default profile uses thresholds of 0.65/0.7/0.75/0.8 for coal/iron/gold/diamond, with upper limits of world top/64/32/0, clipped to world bounds.

`generator.columnAt(x, z)` returns terrain after caves and ores, but excludes trees and structures. When caves or structure rules are enabled, calling `generateInto` with a noise generator replaces the complete target chunk, including clearing old blocks where the new result is air. Without those features, noise generators preserve legacy nonzero-only writes. Plain column functions and flat generators also only write their nonzero entries; they do not clear existing blocks.

## Structure templates

Templates are immutable coordinate/block lists. Build one directly with `createStructure(blocks)` or use every shape method on `StructureBuilder`:

```ts
const tower = new helpers.StructureBuilder()
  .set(0, 0, 0, "stone_bricks")
  .fill(-2, 0, -2, 2, 0, 2, "stone_bricks")
  .hollowBox(-2, 1, -2, 2, 5, 2, "cobblestone")
  .sphere(0, 6, 0, 2, "mossy_cobblestone")
  .cylinder(0, 1, 0, 1, 5, "air")
  .line(-2, 1, -2, 2, 5, 2, "oak_log")
  .add(helpers.createBoulderStructure({ radius: 1 }), 4, 0, 0)
  .build();
```

The presets produce block templates, not entities or loot containers:

| Factory | Dimension options (defaults) | Palette options (defaults) |
| --- | --- | --- |
| `createTreeStructure` | `trunkHeight: 5`, `canopyRadius: 2` | `log: "oak_log"`, `leaves: "oak_leaves"` (persistent when resolved) |
| `createBoulderStructure` | `radius: 2` | `block: "cobblestone"` |
| `createRuinStructure` | `width: 5`, `height: 4`, `depth: 5` | `wall: "mossy_stone_bricks"`, `floor: "stone_bricks"` |
| `createWellStructure` | `radius: 2`, `height: 3` (roof Y, floor at 0, water at 1) | `stone: "cobblestone"`, `water: "water"` |
| `createCabinStructure` | `width: 7`, `height: 4` (roof Y), `depth: 5` | `wall: "oak_planks"`, `floor: "oak_planks"`, `roof: "cobblestone"`, `air: "air"` |

All dimensions and shape coordinates are integers. Box bounds are inclusive. `hollowBox` writes the shell without clearing its interior; write explicit air to carve an existing world. Duplicate positions use the last block. `add(template, x, y, z)` translates a template before composition. In structure templates, named leaves resolve with `persistent: true`, including custom tree species; supply a numeric state ID to control persistence yourself. This differs from the legacy `trees` option described above.

For manual placement, `placeStructure(world, structure, x, y, z, options?)` clips blocks outside world Y bounds. `chunkX` and `chunkZ` together restrict writes to one chunk; `replace: "air"` only fills targets with state ID 0 (default `"all"` overwrites). `transformStructure` and placement options support `rotation: 0 | 90 | 180 | 270`, `mirrorX`, and `mirrorZ`. Mirrors apply before rotation, around local `(0,0,0)`. These transform coordinates only—they do not change directional block-state properties. Resolve stairs, doors, and other directional states yourself when rotating custom templates.

```ts
helpers.placeStructure(world, helpers.createWellStructure(), -2, 80, 15, {
  rotation: 90,
  chunkX: -1,
  chunkZ: 0,
});
```

`createStructureHelper(version)` binds placement and preset factories to a `ProtocolVersion`; it also exposes `resolveStructure(template)` to compile names into numeric states. `helpers.forVersion(protocol)` includes the same bound methods plus the shared builder, transforms, and noise helpers.

### Automatic placement

Each `structures` entry accepts:

| Option | Default | Meaning |
| --- | --- | --- |
| `id`, `structure` | required | Unique stable placement ID and template |
| `spacing` | `64` | Candidate-cell width in **blocks**, not chunks |
| `separation` | `8` (capped) | Blocks excluded from each cell's high edge |
| `chance` | `1` | Seeded chance that a cell receives a start |
| `salt` | `0` | Extra number/string seed component |
| `rotate` | `true` | Pick a seeded quarter-turn coordinate rotation |
| `y` | surface | Fixed origin Y, useful underground |
| `offsetY` | `0` | Offset after fixed/surface anchoring |
| `minY`, `maxY` | world bounds | Inclusive limits on final origin Y |
| `maxSlope` | `3` | Maximum footprint terrain-height difference |
| `allowUnderwater` | `false` | Permit submerged surface footprints |
| `foundation` | off | Fill under occupied bottom-layer blocks to terrain |
| `minDistanceFromOrigin` | `0` | Minimum footprint distance from `(0,0)` in blocks |

Without fixed `y`, the template's minimum local Y is placed one block above the highest terrain column in its entire footprint. A negative `offsetY` can embed rocks into the ground. With fixed `y`, surface slope, underwater, and foundation behavior do not apply. Automatic placements are rejected if any template block exceeds world Y bounds; manual placement clips instead. `structuresAt(chunkX, chunkZ)` takes chunk coordinates and returns starts whose transformed templates intersect that chunk, including starts anchored elsewhere; the returned `y` is already fixed.

Placement is deterministic across chunk boundaries. Rules are sorted by `id`, and when structures overlap, lexical-later IDs write last. `separation` does not account for template size, so large templates can overlap one another even in neighboring cells.

## Per-chunk generation

```ts
const generator = helpers.createNoiseGenerator({ seed: "mc-protocol" });
const world = new helpers.ChunkHelper();
helpers.generateInto(world, chunkX, chunkZ, generator);

generator.heightAt(x, z);              // world block coordinates
generator.columnAt(x, z);              // world block coordinates
generator.structuresAt(chunkX, chunkZ); // chunk coordinates
```

`generateInto` also accepts `(x, z) => number[]`, with state IDs from `minY` upward and `0` as air. `createFlatGenerator(layers)` creates such a generator. Generate a chunk's eight neighbors before requesting its lit packet so edge skylight is correct.

For on-demand generation without blocking the server thread, [`createNoiseChunkPool`](/docs/multithreading) distributes built-in noise generation across workers, creates the lighting margin automatically, deduplicates concurrent requests, and caches packet-ready results.

## Noise helpers

`createNoise2D` and `createNoise3D` return seeded continuous noise in `[-1, 1]`. `fbm` and `fbm3D` combine octaves with `octaves`, `lacunarity`, and `persistence`. `hashSeed`, `hashPosition`, and `createRandom` provide deterministic seed and placement primitives.

The terrain system was designed with projects such as [Terra](https://github.com/PolyhedralDev/Terra) and [Iris](https://github.com/VolmitSoftware/Iris) as inspiration; no code was copied from them.

## Scope and limitations

This is a compact custom-world toolkit, not vanilla world generation. A world has one configured biome ID. It does not provide vanilla terrain parity, biome distribution, aquifers, structure loot, or mob spawning. The application remains responsible for game logic and persistence.

Generation cost grows roughly with the number of chunks: radius 3 is 49 chunks, radius 5 is 121, and radius 8 is 289. Generate reusable worlds once or generate chunks on demand.
