Scenes, blocks and beats
Patter’s structure is a tree of Scene → Block → Group → Snippet → Beat. One rule runs through all of it: a container picks among its children, and each child decides whether it’s eligible. So the things you can select (groups, snippets) can carry a condition; the things you address (scenes, blocks) cannot.
There’s a second idea worth holding onto, which is that the tree works at two levels.
Selection is the walk down the tree that decides the next beat. Delivery is what your game sees, a flat stream of beats, pulled one at a time until a choice or the end. The host never sees a snippet or a block; it just pulls beats.
The containers
Section titled “The containers”A scene is the unit of context. It owns the cast, scene-local properties, and a list of effects that run on entry. It holds one or more blocks, and the first block is where you enter (there’s no explicit pointer). The sharpest line between a scene and a block is that scenes run effects when you enter them and blocks don’t.
A block is a named, addressable section. A block always runs its children in order, so it’s never a “pick one” and never conditional. It must have an author name, which doubles as its jump-target label. To pick one of several things inside a block, nest a group.
A group is a container with a condition and an optional selector (see Choices & logic). Groups nest as deep as you like, so one group’s condition can turn a whole subtree on or off.
A snippet is the only leaf, and the smallest playable unit, zero or more beats played as one, optionally followed by a jump. Nothing is re-evaluated inside a snippet. The seam between snippets is the only place interaction can happen.
A snippet holds beats, and there are three kinds.
A line beat is spoken dialogue. It has a character (checked against the cast), an
optional direction for the performer (language-neutral, never localised), an optional
speaker qualifier, and localised text. A voiced line is a fixed string, with no
interpolation.
A speaker qualifier is what a screenplay calls a character extension: TAM (O.S.) for a
character heard but not seen, PLAYER (V.O.) for a thought, GUARD (RADIO) for a voice over a
radio. It says how the line is delivered, not who says it, so TAM and TAM (O.S.) are the same
character everywhere. The line stores the qualifier’s gameId (os), picked from the project’s
list of qualifiers.
A text beat is narration or the author’s voice. It has no speaker, is never voiced, and is
free to interpolate property values.
A game event is an instruction to the engine with no visible words. It carries only Game Data the host reads when the beat plays, so it can play a sound or move a camera. Game event beats never appear in the locale tables.
A line or text beat can also carry padAfter, the pause after it in seconds, from -10 to 60. A
positive pause waits, zero follows at once, and a negative one starts the next beat that long before
this one ends, so the next line cuts in on it. A beat that sets none takes the nearest
padAfterDefault above it (its snippet, any groups around it, its block, its scene), then the
project’s, then 0.6 seconds. A snippet’s last line can’t be cut in on, because what follows it isn’t
certain until the story chooses, so a negative pause there plays as none.
An option’s prompt carries padAfter too. When the
prompt is spoken, its pause times the reply. A line prompt can carry a speaker qualifier, as any line
can.
Every beat gets a stable id the moment it’s created, never based on its content or position. Translations, jumps, cursors, and visit counts all key off that id, so content can move around freely without breaking anything.
A snippet can end with a jump, which fires at the snippet’s closing seam, after
its beats. A snippet that is only a jump (no beats) is a pure routing node. Jumps
target a scene, a block, or the reserved END, never a snippet (a snippet
plays as a whole, so you can’t land partway into one).
There are two kinds.
A jump is the default, and it’s one-way. It heads where it says and drops any pending
returns.
A call heads there and comes back. It remembers where it was, runs the target, and
returns to the next child in the calling block when the target finishes (Ink calls this a
“tunnel”). Calls nest and recurse safely.
A jump can carry a condition: “jump if X, otherwise carry on.” When a snippet just
falls off the end of its block, the dialogue is finished: the same signal as an
explicit → END (a call returns to its caller first).
Choices and selectors
Section titled “Choices and selectors”Branching past a simple run (the branch, sequence, and choice selectors,
options, and their flags) is covered on the next page,
Choices & logic.
Open source under the MIT licence, made by Ian Thomas.