Graph
The chapter flow graph — entry panels, directed edges with transitions, JSON Logic conditions, priorities, and variable mutations.
Every chapter has a required graph: panels are nodes, and directed edges define how the reader moves between them. Edges can carry a transition, a JSON Logic condition for branching, and variable mutations that run when the edge is taken. This is what turns a linear comic into an interactive one.
Graph
| Property | Type | Required | Description |
|---|---|---|---|
entry | Identifier | Identifier[] (min 1, unique) | Yes | The panel where the chapter starts. An array declares multiple possible entry points |
edges | Edge[] | Yes | Directed connections between panels. May be empty since 1.7 (it was min 1 before) |
nodes | object (ID → { label: LocalizedString }) | — | Optional per-node metadata (display labels, e.g. for a chapter map) |
The panel references entry and from are keys of the chapter's panels map. An edge's to is usually one too, but may name a panel of any other chapter of the work — a chapter transition, or a jump into an endings chapter. The reader then continues in the target's chapter. This is part of the format since 1.7.0 (earlier wording required to to be in the same chapter) (see Moving between chapters). Since format 1.7 edges may be empty ("edges": []): a chapter without edges — for example a single-panel chapter — follows its panels' reading order. The property itself stays required.
Edge
| Property | Type | Required | Description |
|---|---|---|---|
from | Identifier | Yes | Source panel ID — a panel of this chapter |
to | Identifier | Yes | Target panel ID — usually of this chapter, but any panel of the work is allowed |
condition | JsonLogic | — | The edge is only eligible when this evaluates truthy against the current variable state |
priority | integer ≥ 0 | — | Ordering hint when multiple edges from the same panel are eligible |
transition | Transition | — | Animation when traversing this edge; overrides the format preset's defaultTransition |
cameraMove | CameraMove | — | Camera travel when this edge is taken in canvas view (1.4+); inherits the format preset's defaultCameraMove. Coexists with transition (which still applies in panel view) |
action | Mutation[] | — | Variable mutations executed when the edge is taken |
label | LocalizedString | — | Reader-facing choice text of this path (1.7+). Players show it on the branch chooser's buttons when several paths leave a panel; without it they fall back to the target panel's title, then to a numbered "Option N" |
mutations | object[] | — | Editor metadata — free-form edge descriptors written by the CMS (e.g. hotspot/edge-type markers). Not variable mutations |
Naming gotcha: on an Edge, variable writes go in action (an array of typed Mutation objects). The mutations property on an edge is unvalidated editor metadata. Hotspot actions, by contrast, do use a property named mutations for variable writes — see Hotspots.
Branching
A panel with several outgoing edges branches on their conditions. Typically a choice panel sets a variable via hotspots, and downstream edges route on it:
{
"graph": {
"entry": "p1-1",
"edges": [
{
"from": "p1-1",
"to": "p1-2",
"transition": { "type": "slide", "dir": "left", "durationMs": 350 }
},
{
"from": "p1-2",
"to": "p1-3-left",
"condition": { "==": [ { "var": "path.choice" }, "left" ] },
"priority": 1
},
{
"from": "p1-2",
"to": "p1-3-right",
"condition": { "==": [ { "var": "path.choice" }, "right" ] },
"priority": 1
},
{
"from": "p1-2",
"to": "p1-3-default",
"priority": 0
}
]
}
}
An unconditional edge acts as the fallback when no conditional edge matches. How the player evaluates eligibility and order at runtime is described in Graph Navigation and State & Conditions.
Mutation
A typed write to a variable, used in Edge.action and in hotspot goTo/setVariables actions:
| Property | Type | Required | Description |
|---|---|---|---|
op | string | Yes | set | increment | toggle | append | remove |
var | string | Yes | Target variable ID, e.g. "path.choice" |
value | JsonValue | — | Operand — the value to set / increment by / append / remove |
{
"from": "p2-4",
"to": "p2-5",
"action": [
{ "op": "set", "var": "story.metCourier", "value": true },
{ "op": "increment", "var": "story.visits", "value": 1 }
]
}
op | Effect |
|---|---|
set | Assign value to the variable |
increment | Add value to a numeric variable |
toggle | Flip a boolean variable |
append | Add value to a list-valued variable |
remove | Remove value from a list-valued variable |
Mutations on readOnly variables are invalid at runtime (the schema cannot cross-check this). Keep decision state in session or chapter scope and preferences in persistent scope — see Variables.
Node labels
nodes attaches display labels to panels without touching the panel definitions — useful for authoring tools and reader-facing chapter maps:
{
"graph": {
"entry": "p1",
"nodes": {
"p1": { "label": { "en-US": "Opening", "de-DE": "Auftakt" } },
"p9": { "label": { "en-US": "Good ending" } }
},
"edges": [ { "from": "p1", "to": "p9" } ]
}
}
Validation notes
- The schema does not verify that
entry,from, andtoreference existing panels, or that every panel is reachable — run graph checks in your pipeline (the CMS does this in Validation). conditionaccepts any JSON shape; malformed JSON Logic only fails at runtime.- Cycles are legal (loops, hubs, replayable scenes).