Layout
A zone says what a region means and how its contents behave — but not where it is. That lives in layout. The layout block holds two things: the seats around the table, and a placement for every zone — its position and size on the felt.
Layout is deliberately kept separate from the zone definitions. Zones carry meaning; layout carries geometry. Because they’re separate, your hand-tuned positions survive regeneration — re-running the pipeline can rewrite a zone’s role or contents without throwing away where you placed it.
Every coordinate here is in millimetres, with the table origin at its centre. Pixel scaling happens only at render time — you author in real table units.
The table
The surface itself has a shape (round, rectangular, or custom), a
size in mm, and an optional surfaceAsset — a felt or board image painted
across the whole table.
What a placement holds
Each zone has exactly one entry in layout.zones, keyed by zone id:
| Field | Type | Meaning |
|---|---|---|
space | table | seatLocal | table = absolute table coordinates; seatLocal = relative to a seat, with one copy instantiated per seat, rotated to that seat |
x / y | number (mm) | Position of the zone box (origin = table centre) |
w / h | number (mm) | Zone box width and height (both positive) |
rotation | number .optional() | Box rotation in degrees |
layer | number .optional() | Draw order when zones overlap |
fitContents | boolean .optional() | Scale contents to fill the box. Default lets the renderer pick: card zones fit, piece/dice/token trays don’t. Set true for a single-component zone you want to resize by resizing the box |
locked | boolean .optional() | Designer-only: the editor ignores the box’s own click/drag (handy for a big background board) — contents stay interactive; ignored at runtime |
bySeat | record .optional() | seatLocal zones only: per-seat geometry overrides keyed by seat index. A seat with no entry uses the base geometry, so the zone stays symmetric until you reposition one seat |
How to edit in the Studio
Open your game → Layout tab. The table is drawn to scale; drag a zone
to move it, drag its handles to resize, and use the Zone inspector to
set rotation, layer, fitContents, and lock. Switching a zone to seatLocal
space mirrors it to every seat at once; nudging one seat’s copy writes a
bySeat override for just that seat. The Table inspector sets the surface
shape, size, and image.
Arranging several zones
There is no separate "align" or "distribute" button — the editor lines zones up three ways. While you drag, a zone’s edges and centre snap to nearby zones and draw guide lines, so two boxes click into alignment as you move them. Select more than one zone (⇧- or ⌘-click) and the Zone inspector offers Match width, Match height, Match size, and Match rotation — these copy the reference zone’s size and angle onto the rest (they don’t move anything). To build a tableau of identical positions from scratch, add a grid zone — a bulk setup placement auto-spreads one component per cell.
Already have a set of zones that drifted out of line — a tableau of slots, a row of supply piles? Ask Pip to arrange or even out them: it reads where each zone currently sits, works out the tidy positions (lining up rows and columns, evening the gaps), and proposes the moves (positions only — sizes stay as you set them), which you review and Save like any edit.
Related
- Seats — where players sit and how
seatLocalrotates - Zones overview — the definitions layout positions
- Setup placements — what starts in each zone