@tamb/gamegrid — a TypeScript library for 2D grid-based web games and interactive matrices.
Also see: site home · interactive demo · GitHub README
Browse the full API on the exports index:
GameGrid / IGameGrid methods stay flat on the instance. TypeDoc groups them by job (not DOM vs data):
| Job | What it does | Members |
|---|---|---|
| Matrix | Logical cells. Writes do not paint. | getMatrix, setMatrix, getCell, setCell, getAllCellsByType, getActiveCell, getPreviousCell |
| Movement | Focus and history. Updates state and events; highlights when mounted. | setActiveCell, moveUp / moveRight / moveDown / moveLeft, moveTo, rewind, rewindTo, unrewind, unrewindTo |
| View | Optional markup. Omit render for headless use. |
refs, render, refresh, refreshCells, destroy |
| State | Authoritative IState. Middleware, no grid CustomEvents. |
getState, setStateSync |
| Options | Runtime toggles. Does not swap the matrix or re-render. | options, getOptions, setOptions |
| Zoom | Viewport window and region tiles. | getZoom, setZoom, clearZoom, getZoomAround, getQuadrantZoom, getFractionZoom, zoomAround, zoomQuadrant, zoomFraction, getRegionAt, getActiveRegion |
setCell / setMatrix write the matrix only. refreshCells / refresh / render paint. refreshCells also writes when you pass cell.
Movement and state use [x, y]: column (x), then row (y). The backing matrix is matrix[row][col] → matrix[y][x].
state.moves is an oldest-first trail of landed [x, y] cells, including the current cell. rewindLimit (default 20) caps its length; the oldest entry drops first. Blocked attempts are not recorded. rewind / rewindTo push dropped coords onto state.future. unrewind / unrewindTo replay that stack. A new landed cell clears future; a blocked stay does not.
grid.moveDown();
grid.moveRight();
grid.rewind(); // back one step
grid.unrewind(); // redo that step
grid.rewind(2); // back two steps (clamps to the oldest remaining)
grid.rewindTo(0); // jump to the oldest remaining index
grid.unrewindTo(2); // jump forward in the combined moves + future trail
rewind / rewindTo emit REWIND (detail.steps, detail.index, plus IMoveEventDetail) then MOVE_LAND. unrewind / unrewindTo emit UNREWIND the same way. They are not rate-limited by moveDebounce.
moveTo(coords) walks one cell or an explicit list of cells through setActiveCell. Each step uses the existing block / collide / wrap / zoom-edge rules. Gaps teleport (no A*). The walk stops when a step does not land on the requested cell.
grid.moveTo([2, 1]);
grid.moveTo([
[0, 1],
[0, 2],
[1, 2],
]);
MOVE_* events (and wrap / boundary events from the same attempt) include IMoveEventDetail: from, to (candidate after wrap/clamp), direction, and blocked. On a successful land, to matches activeCoords. On a block, to is the rejected cell and activeCoords stay at from.
setCell writes matrix[y][x] only. Movement, getCell, getActiveCell, and getPreviousCell see the new type immediately; the DOM (current) does not.
ICell.eventTypes.onEnter / onExit are custom event names dispatched when the active cell changes (detail.coords, detail.cell). onLand / MOVE_LAND also require a real coord change. onDettach fires only when leaving a collide-type cell for a non-collide cell. render() does not run move choreography.
refreshCells({ coords, cell? }) (one item or an array) optionally writes, then replaces those mounted nodes and emits CELLS_REFRESHED. Omit cell after a prior setCell. Use refresh when dimensions or the zoom window change.
See the README flow and ICellRefresh.
Each symbol page in this TypeDoc site includes a Usage / example block from the source comments. Start with GameGrid and IGameGrid.
import GameGrid, { gridEventsEnum, type GameGridDOMEvent } from "@tamb/gamegrid";
const grid = new GameGrid(
{
matrix: myMatrix,
state: { activeCoords: [0, 0] },
options: { wasdControls: true },
},
document.querySelector("#root")!,
);
grid.moveDown();
window.addEventListener(gridEventsEnum.MOVE_LAND, (e: Event) => {
const ce = e as GameGridDOMEvent;
console.log(ce.detail.gameGridInstance.getState());
});
Omit the container argument for headless use, then call render(element) when you need DOM.