Play mode
Walking the story and dressing it — the toolbar, every key, what a save carries, the three container kinds, the five looks and the twenty-one elements.
All documentation — Playing and styling
Boards and the canvas
The node builder
Modelling the world
Variables, logic and commands
Voice and audio
AI
Working together
Project settings
On this page
- Is this the engine the game runs?
- What happens at a node, in order
- A moment
- A blocked choice
- Saving, loading and rewinding
- The toolbar
- Player settings
- Sound on a node
- Keys
- Design — how a moment is drawn
- Keys as — the device preview
- Three container kinds
- Twenty-one elements, in five families
- The five looks
- Player settings a look can set
- Custom CSS
- The Theme Editor is a different thing
- Sharing
- What breaks
Two tabs: Play (walk the story) and Design (dress it). Neither is on the rail. Play in the bar above a board opens the Play tab beside the board and plays that board from its start; the list on the left and the tabs you had open stay where they are. The palette button on the player’s toolbar opens the Design tab on the moment you are looking at, and so does Design how it plays and looks in a node type’s play-mode read-out. Each exists once per project: opening it again brings the same tab to the front.
The player’s Close (Esc) closes the Play tab. Clicking another tab only puts Play behind it, and it carries on from where you left it when you come back.
They are the same renderer over the same project. Neither is a preview of the other, and a public share link runs the same thing again.

Is this the engine the game runs?
Not the same code — the same rules, pinned by shared fixtures.
| In the browser | In Unity | |
|---|---|---|
| Reads | The live project, on every step | A resolved payload built at sync |
| Relationship | — | A port of the browser’s traversal |
The browser’s test suite runs a corpus of fixtures through the real traversal engine and writes both the payload and the expected transcript into the C# test project; C# replays the same transcripts and asserts they still match. Thirty-eight fixture pairs are committed, covering gates, jumps, requirements, roll-forward, board entries and language switching.
What happens at a node, in order
- Run every script slot, in document order.
- Present a step if the node has content.
- Resolve the exits: any exit with content means choose — an edge’s label counts, and when several unlabelled, ungated exits leave the node, each borrows the text of the node it leads to and the player is asked; otherwise the first passing gated exit is taken; otherwise exactly one exit advances; otherwise the story ends.
- A
jumpTargetfield overrides all of that; areturnAfterpushes a frame to come back to.
A moment
| Field | What it is |
|---|---|
node | Its id, its board, its type |
verb | choose or present |
arrivedBy | start · walk · jump · return |
fields | The resolved field values |
choices | The options, including the blocked ones |
choiceKind | exit or boardEntry |
cast, audio, variables | The state on stage |
trace | Why this step happened |
flags | isLatest, isTerminal, awaiting |
awaiting is advance, choice, timer, or nothing. The engine itself is idle, presenting,
awaiting-choice, or finished — and finished is one of end of path, stopped or error.
A blocked choice
An option drawn but not takeable carries: its kind (a gate or a requirement), the failing
expression — diagnostics only, never shown to a player — the tag id and label that become the
[STRENGTH] badge (requirements only), an optional author reason, and the failing terms: one
per failed check, naming the variable, never the tag.
Saving, loading and rewinding
| A save carries | Where you are (board, node, call stack), the whole language runtime state, which exits have been taken, the active language, and the last 50 moments each with a full state copy |
| The runtime state | Variables, the entity overlay, the random generator’s state, visit counts, the current node |
| A save does NOT carry | The moments’ rendered content — it is re-derived, so a save cannot go stale against an edited board. Nor the roll-forward stack, nor which scripts ran, nor anything past the 50-moment window. |
| Where | Your browser, one slot per project |
| Restoring | Every moment is re-drawn from its recorded state. No script re-runs and no visit is re-counted. |
Back steps to the previous moment. Forward re-advances into a step you rolled back, replaying the recorded decision instead of asking it again — which is what makes rewinding useful for testing rather than merely undoing.
Variable presets let you start mid-story with the state you would have arrived with. The presets themselves live in the project; which one is active is per device.
The toolbar
Controls marked Author are absent — not disabled — for someone playing a share link.
Every tooltip below is quoted exactly, in the order the controls appear.
| Tooltip | Shown to |
|---|---|
| Player settings | Everyone |
| Why this step? (debug) | Author |
| Design this look (opens the Design tab) | Author |
| Share this story (make a link someone else can play) | Author |
| Back (Backspace) | Everyone |
| Forward — replays the decision you rolled back (→) | Everyone |
| Restart | Everyone |
| The step counter | Author |
| Save this playthrough on this device | Everyone |
| Load the saved playthrough | Everyone |
| Language | Everyone, only when the project has more than one |
| Fullscreen / Leave fullscreen (Esc) | Everyone |
| Close (Esc) | Author, in the Play tab only — it closes the tab |
Player settings
- Text speed — Slow · Normal · Fast · Instant
- Accessibility — Text size, 100–200% in steps of 10; and Turn timed choices off
- Play voice-over — ticked by default, and offered whether or not the project has any recorded lines yet. While it is ticked a Volume slider sits beside it, 0–100% in steps of 5, starting at 100%. The two are remembered on this device for each project, and they are the only player settings that outlast the visit; turning voice-over off and on again keeps your level. The same switch and slider govern sound effects too — there is no separate effects volume.
Sound on a node
A node whose Asset field holds an audio file plays that sound once, when the story shows the node. It never holds the story up: a sound still playing when you move on runs to its end, unless the next node carries a sound of its own. A picture or video in an Asset field is drawn, never played. If a node carries several sounds, only the first plays.
Keys
| Key | Action |
|---|---|
Space / Enter | Skip a running reveal, otherwise advance |
1–9 | Take that option — claimed only when the option exists |
Backspace | Back |
→ | Roll forward — claimed only when there is something to replay |
Escape | Close |
Delete | Remove the selected design box (never Backspace) |
Ctrl+Z and Ctrl+Y / Ctrl+Shift+Z are handed back to the editor while you are designing.
Design — how a moment is drawn
Keys as — the device preview
The Design tab’s bar carries Keys as: Keyboard & mouse · Xbox · PlayStation · Switch · Touch. A mention of an input action — a record whose definition draws its mentions as a keycap (see definitions) — draws the way that device would: the Xbox pad’s B button, the PlayStation circle, the Switch’s A, or the gesture. Every line on the stage and in a log redraws at once, and nothing is replayed. Nothing it shows is stored, and a game draws the player’s own binding instead. The Play tab and a share link draw the keyboard.
The pictures are glyphs, drawn in the stage’s text colour; a control the glyph set does not draw
shows its words in a keycap instead. A Stage’s CSS can reach a glyph keycap through
.mention-keycap--glyph, its .mention-keycap__glyph and its visually hidden
.mention-keycap__text.
Three container kinds
| Kind | The vocabulary it speaks to its children | Options |
|---|---|---|
stack | A row or a column | direction, gap, padding, align, justify, wrap |
grid | Cells | columns (a track list, not a count), rows, gap, padding, align, justify |
overlay | Nine named points | padding; the child answers with its point, an offset and a z-order |
Twenty-one elements, in five families
| Family | Element | What it is |
|---|---|---|
| Layout | Stack | Boxes in a row or a column |
| Grid | Boxes in columns and rows you set | |
| Overlay | Boxes stacked on top of each other | |
| Spacer | Empty space | |
| Scroll | A region that scrolls on its own | |
| Text | Text | One line of text |
| RichText | Formatted writing, with its styling kept | |
| TextReveal | Text that types itself out | |
| Data | FieldValue | Whatever a field of this node holds |
| Portrait | A picture of a character | |
| Media | An image, a sound or a video | |
| Meter | A bar for a number — health, trust, time | |
| Die | What the story rolled — the die, the total, the outcome | |
| Tally | Records with counts — what was handed over, or a field’s list | |
| Flow | ChoiceList | The options the player can take |
| Choice | One option, drawn your way | |
| AdvanceHint | The “press to continue” affordance | |
| Timer | A countdown on a timed choice | |
| Structure | Log | The story so far, line after line |
| StageSlot | Whoever is on stage right now | |
| Card | This node, drawn the standard way |
Eight of the twenty-one hold children. Card deliberately does not.
Values in the text are live. A {…} value in a line or in an option’s text — a variable or an
expression, inserted with { while writing in a node — is
filled with its value when the step is drawn: You have {gold} coins plays as You have 120
coins. A value that cannot be worked out shows its own source, such as {gold}, never a blank.
Text you typed with literal braces stays as you typed it.
ChoiceList draws tiles when the options carry pictures. When every option is a row of a list
whose rows carry a port, and at least one of those rows holds a picture — a media field, or a record
with a portrait — the options become tiles instead of lines: the picture on top, the row’s title
under it, and its next text as one small line. A row whose picture has not loaded yet draws an empty
frame. The number keys and a click still pick by position, and a locked option is dimmed and
explained exactly as in a list. Only the default drawing becomes tiles: a ChoiceList you have filled
with your own Choice children keeps them, and the board-entry menu is never tiled. A Stage’s CSS can
reach them through .ts-choicelist--tiles, .ts-tile, .ts-tile__face, .ts-tile__title and
.ts-tile__line.
The Die shows on a step whose script rolled — a Skill check’s roll(20), or any script of
your own that calls roll, pick or another random builtin — and on no other; a story that never
rolls never sees it. A check that says nothing to
the player is not a step of its own, so its roll rides to the next line that is: the die lands
on the line after the check, with the verdict beside it. Its three knobs name the
variables the verdict is read from (Total from, Target from, Outcome from), defaulting to the
Dialogue, studio grade pack’s checkTotal, checkDifficulty and checkPassed; a Scripted check that writes its
own names points them there. No look ships one — place it where the roll should land, usually
between the line and the options.
The Tally shows records with counts — a tile per record, its picture with the count in the corner — under a heading you name (Call it; Received unless you say otherwise). Left unbound, it shows what the step handed over: the items on the outcome a Loot roll drew, and nothing on a step that handed nothing over. Bound to an Entities with counts field — a Recipe’s ingredients, a shop’s stock — it shows that field’s entries instead, so the same element is Received on one Stage and Ingredients on another. The same record listed twice is two tiles. No look ships one.
The five looks
| Look | |
|---|---|
| JRPG box | |
| Subtitle + list | |
| Big portrait | The default |
| Side portrait | |
| NVL page |
Each carries a palette as well as a layout, and the text colours are derived from it by contrast measurement rather than chosen. Hex only.
Player settings a look can set
revealSpeed (default 40), autoAdvanceMs (default none), timerPolicy (default none) and
appendText (default false).
Timer policy is one of three: highlighted default, silence is a choice, or interrupt and restart. With none chosen, the Timer element is withheld.
A look is stored in the project, so it travels to your collaborators and to a share link.
Custom CSS
Two surfaces, and conflating them is the usual mistake:
| Custom CSS | Style classes | |
|---|---|---|
| What it is | A raw stylesheet | Property rows that define named classes |
| Layer | ts-author — the outermost | ts-stage |
| Can express a selector | Yes | by design |
Layer order, emitted before any rule: ts-base, ts-theme, ts-layout, ts-stage, ts-author. The
player’s own chrome is deliberately unlayered, so author CSS cannot hide the way out.
Custom CSS now lives on the look, in the project. An older per-device copy is offered for adoption on a gesture rather than migrated automatically, and archived rather than deleted.
The Theme Editor is a different thing
The Theme Editor at the bottom of the activity bar themes the TaleShard editor’s own chrome, not your game — see the workspace. A game’s look is the Design tab above. The two share no settings.
It is stored per browser and never shared with collaborators.
Sharing
A share link comes in three shapes: anyone with the link, link plus password, or a named link per person, individually revocable.
What breaks
| Situation | What happens |
|---|---|
| A gate errors | The exit reads false, recorded in the trace |
| A gate is present but blank | On an automatic exit the exit fails; on an option the player picks it is no condition — see boards |
| A script errors | Script error in “«field label»”: «error» — and the step still happens. Each script slot runs separately so the message can name the field. |
| A save is loaded against an edited project | Dropped moments, entities and variables are reported, not silently discarded |
| A playthrough is older than 50 moments | The window is what a save carries; earlier moments are gone |
| Author CSS tries to hide the toolbar | It cannot — the player’s chrome is outside every author layer |