Getting started
dungeonmaker-js generates procedural dungeons in JavaScript. It is an unofficial WebAssembly port of DungeonMaker 2.05 by Peter Henningsen (with contributions by Aaron Dalton), the C++ generator written in 2001 (copyright 2001 Dr. Peter Henningsen) and released as 2.05 in 2005. The package compiles the original generator to WebAssembly, runs it in a Web Worker and wraps it in a typed API, a set of renderers and exporters, and a command line tool. The same design and seed always produce the same dungeon, on every platform.
The package is released under GPL-3.0-or-later; read License before you ship it inside an application. Want to see it first? The Playground generates dungeons in your browser.
Install
pnpm add dungeonmaker-jsnpm install dungeonmaker-jsyarn add dungeonmaker-jsbun add dungeonmaker-jsThe package is ESM only. Its entries are:
dungeonmaker-js: client and design helpersdungeonmaker-js/presets: the eleven upstream designs as JSONdungeonmaker-js/schema:validateDesignand the TypeBox schemadungeonmaker-js/core: the bare WebAssembly core (loadCore()), takes legacy design textdungeonmaker-js/worker: the worker module, for theworkerescape hatchdungeonmaker-js/render: ASCII, tiles, RGBA, canvasdungeonmaker-js/export: dungeon JSON, PNG, Tiled.tmj, ASCII, rawdungeonmaker-js/node:generateToFiles, Node onlydungeonmaker-js/design.schema.jsonanddungeonmaker-js/dungeon.schema.json: the JSON Schemas of the design and the dungeon file
Quick start (browser)
import { createDungeon } from 'dungeonmaker-js';
import { design3A } from 'dungeonmaker-js/presets';
const dungeon = await createDungeon({ design: design3A, seed: 42 });
// dungeon.width, dungeon.height, dungeon.cells (Uint8Array, row-major), dungeon.get(x, y)createDungeon runs the generator in a Web Worker, so the page stays responsive. The worker and the wasm file are found through new URL(…, import.meta.url); Vite and webpack 5 need no configuration (see Bundlers). More on the client in Worker client.
Quick start (CLI)
pnpm dlx dungeonmaker-js generate --preset design3A --seed 42 --format json,png --out ./dungeonsnpx dungeonmaker-js generate --preset design3A --seed 42 --format json,png --out ./dungeonsyarn dlx dungeonmaker-js generate --preset design3A --seed 42 --format json,png --out ./dungeonsbunx dungeonmaker-js generate --preset design3A --seed 42 --format json,png --out ./dungeonsThis writes design3A-42.dungeon.json and design3A-42.png into ./dungeons. All commands and options are listed in Command line.
Example (Node)
import { readFile } from 'node:fs/promises';
import { loadCore } from 'dungeonmaker-js/core';
const core = await loadCore();
const design = await readFile('my-design.txt', 'utf8'); // DungeonMaker design text
const { width, height, cells } = core.generate(design, 42);
// cells: Uint8Array, row-major, north-up: cells[y * width + x]The same design text and seed always produce the same dungeon, on every platform.
JSON designs and presets
import { extendDesign, toDesignText } from 'dungeonmaker-js';
import { loadCore } from 'dungeonmaker-js/core';
import * as presets from 'dungeonmaker-js/presets';
const design = extendDesign(presets.designEmpty, { map: { width: 80, height: 60 } });
const core = await loadCore();
const { width, height, cells } = core.generate(toDesignText(design), 42);The presets are shared objects: derive variants with extendDesign, never mutate a preset. toDesignText throws a DesignValidationError (with a JSON Pointer per issue) for a design the generator cannot run, for example rooms or builders that fall outside a smaller map.
Add "$schema": "https://unpkg.com/dungeonmaker-js/design.schema.json" to a JSON design for editor autocomplete and validation; validateDesign from dungeonmaker-js/schema checks a design at runtime. Old design files are read with parseDesignText. Every field is listed in the design config reference; the ideas behind them are in Design concepts.
Bundlers
Vite (build and dev) and webpack 5 (--mode production) build the client without configuration; pnpm bundler:smoke in the repository verifies this against the packed tarball. webpack 5 production builds print a size hint for the ~264 KiB wasm file (~90 KiB gzip); it is harmless, and performance.assetFilter silences it.
If your bundler cannot resolve the worker or the wasm file, pass them explicitly:
const maker = createDungeonMaker({
wasmUrl: '/assets/dungeonmaker.wasm', // the file at `dungeonmaker-js/dungeonmaker.wasm`
worker: () => new Worker(new URL('./dm-worker.js', import.meta.url), { type: 'module' }),
});with dm-worker.js containing only
import 'dungeonmaker-js/worker';Content Security Policy
A strict policy works with these directives:
Content-Security-Policy: default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; worker-src 'self''wasm-unsafe-eval' is required because the worker calls WebAssembly.compile. The worker script and the wasm file must be served from your own origin; worker-src covers the worker.
Where to go next
- Worker client: options, queueing, timeouts and errors.
- Output layers: rooms, entities and generation steps.
- Rendering and export: ASCII, tiles, pixels, PNG, Tiled and JSON.
- Command line and Generate offline, ship data.
- Orientation and cell values: axes and the meaning of every cell value.
