Have more questions? Join our

This documents framework v1, which is deprecated. Games already on it keep running and their authors can keep editing them, but new games are created on the current framework — start with React frontends.

Core concepts

BoardWeaver is a TypeScript platform for building turn-based multiplayer games.

A BoardWeaver game is a tree of TypeScript files under /src, entered at /src/game.ts, that exports gameConfig and a fixed set of hooks. The host runs this loop on every action:

// 1. Author module exports gameConfig + hooks (see `source-files`).
// 2. Engine calls gameConfig(params) once → initial GameState (pieces, spaces, players).
// 3. preGameInitialization?(state)            // optional, runs once
// 4. Per turn:
//    a. getSelectableItems(state) → SelectableItem[]   // what the viewer may click
//    b. user clicks → Action ("SpaceClick" | "PieceClick" | "ButtonClick")
//    c. applyActions(state, action)            // mutate state in place
//    d. getPlayerScores(state) → Scores
//    e. isGameOver(state, scores) → boolean
// 5. Engine publishes new state (privateState scrubbed per viewer) → clients re-render.

State mutation only happens inside applyActions and preGameInitialization. Every other hook (getSelectableItems, getPlayerScores, isGameOver, getButtons, getLayout, renderOverlay, every render()) is read-only — the framework deep-freezes the GameState it passes in. Writes from a read-only context throw a TypeError.

Authority is server-side. The client never decides what's legal — getSelectableItems gates input, applyActions runs server-side, the new state is broadcast.

Mental model

  • Piece = a movable thing (token, card). Instance of a PieceDef subclass. See pieces.
  • Space = a container that pieces sit in (board cell, hand, deck). Instance of a SpaceDef subclass. See spaces.
  • Player = a seat. Number id. See game-state.
  • kind = the class identifier on a Def. Used everywhere to filter and narrow (state.pieces("x-token")). The platform types it via the kind registry — you don't wire that up.
  • Three state buckets decide what data lives where and who can see it:
    • static — constants for a kind: card name, cost, ability text, artwork. Never serialized.
    • publicState — per-instance, visible to everyone: tapped flag, damage, position counter.
    • privateState — per-instance, owner-only. The framework nulls it on the wire for other viewers, so hidden info (a card's identity in hand) never leaves the server.
  • BWSS (Board Weaver Styling System) is what render() returns: a small JSON tree of Box (flex container, children can be nodes or text strings), Image (a GameImage key), and Svg nodes, styled with a fixed, flexbox-like set of properties. Spaces add a Pieces node marking where laid-out piece children go.
  • The GameState API is how you read and write everything: state.piece(...) / state.pieces(...) / state.space(...) / state.spaces(...) (by id or predicate, optionally kind-first for typing), state.currentPlayer, state.activePlayerIds (set this to pass the turn), state.metaData (free-form game-wide scratch state), and state.addPiece / state.addSpace to spawn entities mid-game. Move a piece by assigning piece.spaceId = targetSpace.spaceId.

Where code lives

A game's source is an arbitrary tree of TypeScript files under /src. The build bundles it as a real module graph starting from the entry point, so files import each other by relative path (./pieces/Knight) or absolute /src path (/src/pieces/Knight). The only off-tree import allowed is the "boardweaver" runtime; bare imports (no node_modules) fail the build.

Path Holds
/src/game.ts Required entry point. Exports gameConfig + the game-logic hooks. Small games can live entirely here.
/src/pieces/ One PieceDef subclass per file by convention (e.g. /src/pieces/Knight.ts). See pieces.
/src/spaces/ One SpaceDef subclass per file by convention (e.g. /src/spaces/Tile.ts). See spaces.
/src/*.ts (anything else) Shared libraries — helpers, constants, type aliases. Ordinary modules with no special treatment; import them from anywhere in the tree.

Keep gameConfig and the exported hooks in /src/game.ts; pull pieces, spaces, and reusable logic into their own files and import them. Non-code assets live outside /src in their own namespaces (image keys under /art/, game metadata in /manifest.json), so they don't go through the code bundler.

Where to Find More

This summary is enough to start. For exact signatures and worked examples, pull the docs with the tools available to you — don't guess at an API:

  • get_doc_section — full reference for one section.
  • get_example — complete source of a working game.