Scenes, blocks & beats
Patter’s structure is a tree: 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: 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”- Scene: 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: scenes run effects when you enter them, blocks don’t.
- Block: a named, addressable section. A block always runs its children in order; 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.
- Group: 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.
- Snippet: 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:
- line: spoken dialogue. It has a
character(checked against the cast), an optionaldirectionfor the performer (language-neutral, never localised), and localised text. A voiced line is a fixed string, with no interpolation. - text: narration or the author’s voice. No speaker, never voiced, and free to interpolate property values.
- game event: an instruction to the engine with no visible words. It carries only Game Data the host reads when the beat plays: play a sound, move a camera. Game event beats never appear in the locale tables.
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:
- jump (the default): one-way. It heads where it says and drops any pending returns.
- call: head there and come 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.
MIT-licensed open source · Made by Ian Thomas · patterkit.com