Skip to main content

Low-level 2D terrain rendering

@rpgjs/render-map2d is the framework-independent terrain renderer used by the RPGJS Studio editor and game runtime. It does not load files or URLs and does not depend on the DOM, CanvasEngine, Pixi, or Angular. Decode source images in the host application and pass their RGBA bytes to the renderer.

Install

Render a map or viewport

atlasRgba and controlTextureRgba have the shape { width, height, pixels: Uint8ClampedArray }. The returned frame contains the same shape plus its world x and y. A Canvas adapter may construct an ImageData; a Pixi adapter may upload the buffer as a texture. The renderer does not make either choice. Call prepareTerrainMap() once after map or texture data changes, reuse the prepared value across viewport renders, and call disposeTerrainMap() when the map is unloaded. Omit timeMs for a deterministic static result. Supplying the same inputs and time always produces the same bytes.

Presets

Built-in identifiers are available through TerrainPreset: GrassEdge, Carpet, Water, RoadTownSidewalk, and RoadTownMarked. Map metadata stores these identifiers through its normal render modes. Applications can add a local preset without global registration:
Preset callbacks receive only typed arrays, numbers, and serializable parameters. They must return exactly width * height * 4 RGBA bytes. Keep namespaced custom keys stable because maps persist the shader key.

Worker usage

The normalized map, raster images, render bounds, and returned pixel buffer are structured-clone compatible. Transfer the underlying ArrayBuffer when posting large textures or frames between a Web Worker and its host to avoid copying. Invalid inputs throw TerrainMapValidationError. Its path property identifies the invalid field, for example textures.water.pixels.

Morphology adapters

Canvas and Pixi adapters can share the exact recessed-liquid geometry while they progressively migrate their rasterization to renderTerrainRegion():
At full height, wallAlpha and dropY are zero. inset and the returned edge metrics remain deterministic, which keeps the water contact border identical between the Studio editor and game renderer. The helper accepts and returns only numbers and plain objects.

Texture-derived liquid colors

resolveTerrainLiquidPalette(image, region?, fillColor?) is the shared palette resolver used by Studio’s Canvas renderer and this CPU renderer. It samples up to 1024 opaque RGBA pixels inside region (the whole image by default), returning { base, highlight, shadow } RGB tuples. Bright and dark accents retain sampled saturation instead of mixing systematically with white.
No material-name rule is involved. When opaque pixels are unavailable, the optional hex or RGB fillColor supplies the palette; otherwise the result is null and renderers omit colored accents. Water borders respect border: false and foam: false. Presets receive an optional decoded, cropped source texture via TerrainPresetInput, so the built-in water preset follows the same policy. Prepared render jobs cache cropped textures and palettes; create a new prepared map after changing texture pixels or atlas regions and dispose the old one. Standalone palette users should cache by texture revision and region themselves.