Phaser AE

Streamed Terrain World

Free packv3.0.0-43DWorld

Procedural ground that goes on for kilometres without a world-sized height array. A window of tiles follows the camera, built under budgets you set and recycled as you travel, and the edits you make are still there when you come back.

This is a Free pack. Every phaser.io account can use it in their games.

Released October 14, 2026 · updated October 20, 2026

Streamed Terrain World: a screenshot of its demo
A note about these graphics

Expansion packs don't come with graphics. This screenshot is one way of drawing what the pack does, made for its demo: none of the sprites, tiles or backgrounds in it are in the pack.

That's by design. A pack is a game's logic, mechanics and effects, and it works with whatever art you give it. Some packs draw effects, backdrops or 3D scenes of their own, but none carries a game's sprites.

So the look is yours. Make the art on phaser.io, bring your own, or let your agent draw it in code: the same pack can look like anything.

The pack

terrainWorld3d

function terrainWorld3d(options: TerrainWorld3dOptions = {}): Pack<{ terrainWorld: TerrainWorld3dApi }>

Recycles mesh and atlas slots while travelling; no world-sized height array is allocated.

Classes

TerrainTileStore

class TerrainTileStore

Sample storage stays bounded as travel proceeds. Only explicitly edited samples persist.

  • readonly tileSize: number — World width and depth of one tile; default 192.
  • readonly resolution: number — Quads along each tile side: 16, 32, 64, 128 or 256; default 64.
  • readonly cell: number — World width of one sample cell: tileSize / resolution.
  • readonly amplitude: number — Vertical scale of the built-in hills generator, in world units; default 160.
  • readonly cacheTiles: number — Most source tiles kept in the least-recently-used cache, 1 to 1024; default 64.
  • constructor(options: TileStoreOptions = {})
  • get residentTiles() — Source tiles in the cache now, never more than cacheTiles.
  • get editedSamples() — Authored height samples held, which survive cache eviction; at most 1,000,000.
  • heightAt: (x: number, z: number) => number — Ground height at world (x, z), in world units, triangle-exact and without loading a tile. Throws for a coordinate that is not finite or lies beyond 1,000 km.
  • normalAt: (x: number, z: number) => { x: number; y: number; z: number; } — Unit surface normal at world (x, z), from finite differences one cell either side; throws for a coordinate heightAt refuses.
  • tile(tx: number, tz: number): Heightfield — The source heightfield for tile (tx, tz), counted in whole tiles; built on first use, then cached. Throws for non-integer indices or a tile beyond 1,000 km.
  • edit(edits: TerrainEdit[]) — Atomic offline edits. Shared edge samples are committed once, independent of tile order.
  • save() — Persist with the same generator configuration; no procedural samples are stored.
  • load(document: ReturnType<TerrainTileStore['save']>) — Replace the authored samples with a document from save(). Throws, leaving the store unchanged, if it is not version 1, was saved with another tile size or resolution, has more than 1,000,000 samples, or holds an invalid sample.

Types

TerrainWorld3dApi

interface TerrainWorld3dApi

Large-world terrain, sampled on a stable world-space lattice.

  • heightAt(x: number, z: number): number — Triangle-exact ground height without allocating a tile.
  • normalAt(x: number, z: number): { x: number; y: number; z: number } — Surface normal from neighbouring authoritative samples.
  • edit(edits: TerrainEdit[]): void — Sculpt offline; authored samples survive tile eviction.
  • save(): ReturnType<TerrainTileStore['save']> — Save sparse authored samples; restore with the same generator configuration.
  • load(document: ReturnType<TerrainTileStore['save']>): void — Restore sparse authored samples and queue resident geometry for refresh.
  • readonly tiles: number — Current rendered tile count, bounded by (2 * radius + 1) squared.
  • readonly cachedTiles: number — Number of resident source heightfields in the bounded LRU cache.
  • readonly triangles: number — Resident triangles before frustum culling.
  • readonly pending: number — Tile builds waiting for the per-tick budget.

TerrainWorld3dOptions

interface TerrainWorld3dOptions extends TileStoreOptions

Camera-centred terrain paging with explicit CPU, mesh and generation budgets.

  • radius?: number — Tile radius around the camera, 1..6. Default 3 (49 resident mesh slots).
  • buildsPerTick?: number — Maximum new tiles or LOD changes per simulation tick, 1..8. Default 2.