6 game generators with deterministic seeded output, SVG rendering, and a headless SDK for server-side consumers.
Embed a hex map generator into any page. The same seed always produces the same map.
<iframe
src="https://engine.moddable.games/play/?embed=1&game=nukes&style=artistic&random=1"
style="width:100%;max-width:560px;aspect-ratio:1/1;border:none"
title="Hex Map"
></iframe>
| Param | Values | Default |
|---|---|---|
embed | 1 | Required for embed mode |
game | nukes | twilight | colony | talisman | mongo | endless | Required |
style | artistic | classic | kenney | realistic | artistic |
size | Integer (ring count) | Game default |
players | Integer | Game default |
seed | Any string | Random |
random | 1 | Generate with random seed |
bg | Hex colour (no #) | Transparent |
Control the embed without reloading:
// Switch game
iframe.contentWindow.postMessage({ type: 'hexmap:setGame', game: 'twilight', style: 'classic' }, '*')
// Change visual style
iframe.contentWindow.postMessage({ type: 'hexmap:setStyle', style: 'kenney' }, '*')
// Generate new map
iframe.contentWindow.postMessage({ type: 'hexmap:regenerate', random: true }, '*')
iframe.contentWindow.postMessage({ type: 'hexmap:regenerate', size: 5, players: 6 }, '*')
iframe.contentWindow.postMessage({ type: 'hexmap:regenerate', seed: 'my-seed' }, '*')
// Request data
iframe.contentWindow.postMessage({ type: 'hexmap:getMap' }, '*')
iframe.contentWindow.postMessage({ type: 'hexmap:exportSvg' }, '*')
// Change background
iframe.contentWindow.postMessage({ type: 'hexmap:setBg', bg: '#0B0F1A' }, '*')
// Export PNG (canvas-rendered)
iframe.contentWindow.postMessage({ type: 'hexmap:exportPng', scale: 2 }, '*')
// Edit a hex (cycle terrain type, games that support it)
iframe.contentWindow.postMessage({ type: 'hexmap:editHex', q: 0, r: 1 }, '*')
window.addEventListener('message', (e) => {
switch (e.data.type) {
case 'hexmap:ready':
// { game, seed, size, players }
break
case 'hexmap:mapData':
// { hexes: [{q, r, type, ...}], seed, game, structured }
// structured: game-specific export (e.g. Twilight tile IDs)
break
case 'hexmap:svgData':
// { svg: '<svg ...' }
break
case 'hexmap:pngData':
// { png: 'data:image/png;base64,...', width, height }
break
case 'hexmap:hexEdited':
// { q, r, type } — after editHex command
break
}
})
Headless SDK for server-side tools, APIs, and direct browser consumption.
import {
listGames,
generate,
renderSvg,
getHexInfo,
exportGameData,
editHex,
computeFov,
pathfind,
HexMath,
HexSvg,
createSeededRng,
} from 'moddable-engine/hex-sdk'
Returns all registered games with metadata.
const games = listGames()
// [{ key: 'nukes', label: 'nukes', defaultSize: 5, defaultPlayers: 4,
// orientation: 'pointy', styles: ['artistic','classic','kenney'] }]
Generate a map. Same seed always produces the same result.
const result = generate('nukes', { size: 4, players: 4, seed: 'abc123' })
// { hexes: [{q, r, type, ...}], annotations: [...], seed, game, size, players }
Generate and render as SVG in one call. No DOM required.
const { svg, hexes, seed } = renderSvg('twilight', {
seed: 'galaxy42',
style: 'classic',
bgColor: '#0a0d2a'
})
// svg = '<svg ...' (~10-15KB)
Look up a hex and its neighbours.
const info = getHexInfo(result.hexes, 0, 0)
// { hex: {q, r, type: 'mount'}, neighbours: [...], distance: 0 }
Generate a map and export in game-specific structured format.
const result = exportGameData('nukes', { size: 4, seed: '12345' })
// { game: 'nukes', format: 'nukes', data: {...}, seed: '12345' }
const result = exportGameData('twilight', { size: 3, seed: '12345' })
// { game: 'twilight', format: 'hex', data: { hexes: [...], seed, size, players } }
Cycle a hex through terrain types (games that support terrain editing).
const hex = editHex('nukes', hexes, 0, 1)
// { q: 0, r: 1, type: 'desert' } — type has been cycled
// Returns null for games without terrain editing or invalid coords
Line-of-sight field-of-view from a hex. Blocking terrain creates shadows.
const result = computeFov(hexes, { q: 0, r: 0 }, {
range: 3, // max distance (default: 3)
blocking: ['mountain'] // terrain types that block LOS
})
// { origin, range, blocking, visible: [{q,r,type,distance}], blocked: [...] }
BFS shortest path between two hexes, respecting impassable terrain.
const result = pathfind(hexes, { q: -2, r: 0 }, { q: 2, r: 0 }, {
impassable: ['mountain', 'water'] // terrain types that cannot be traversed
})
// { reachable: true, from, to, distance: 4, path: [{q,r,type}, ...] }
// { reachable: false, from, to, path: null } — when no path exists
| Key | Game | Sizes | Styles |
|---|---|---|---|
nukes | Nukes (area control) | 2–6 rings | artistic, classic, kenney |
twilight | Twilight Imperium (galactic) | 3–8 players | artistic, classic |
colony | Colony (resource/trade) | 3–4 rings | classic, kenney, realistic |
talisman | Talisman (fantasy adventure) | 3–5 rings | artistic, classic |
mongo | Planet Mongo (pulp sci-fi) | Fixed layout | artistic |
endless | Endless Skies (exploration) | Fixed layout | artistic |
Axial coordinate utilities for hex grids.
import { HexMath } from 'moddable-engine/hex-sdk'
HexMath.getNeighbors(q, r) // [{q, r}] — 6 adjacent hexes
HexMath.axialDistance(a, b) // integer distance between two {q, r} points
HexMath.hexRing(center, radius) // all hexes at exactly N distance
HexMath.hexSpiral(center, radius) // all hexes within N distance
Low-level SVG renderer with tile images, labels, and overlays.
import { HexSvg } from 'moddable-engine/hex-sdk'
const svg = HexSvg.toSVG(hexes, {
size: 30,
orientation: 'pointy', // or 'flat'
bgColor: '#1a1a2e',
colors: { mount: '#4a3728', plains: '#3d6b35' }
})
// With annotations (highlights, tokens, arrows, legend)
const svg = HexSvg.toAnnotatedSVG(hexes, annotations, opts)
Tile set metadata available at /api/tiles/index.json.
const res = await fetch('https://engine.moddable.games/api/tiles/index.json')
const data = await res.json()
// { sets: [{ id, label, game, tileCount, path }] }
8 tile sets: nukes, nukes-kenney, talisman, talisman-kenney, colony, mongo, endless, twilight.