Manifest ReferenceVariables

Variables

The Variables section — typed variable definitions with five scopes, defaults, visibility, and persistence for branching PanelWave stories.

The optional variables section declares the state that drives everything conditional in a PanelWave story: branching edges, conditional layers and speech bubbles (visibleIf), panel variants (when), and hotspot side effects. Variables are read in JSON Logic expressions via { "var": "path.choice" } and written through mutations.

Variables has a single optional property: definitions, an array of unique VariableDefinition objects.

VariableDefinition

PropertyTypeRequiredDefaultDescription
idstringYes—Variable name; pattern ^[a-zA-Z0-9][a-zA-Z0-9_]*(?:\.[a-zA-Z0-9_-]+)*$ — letters, digits and underscores, optionally in dot-namespaced segments, e.g. prefs.hints, path.choice, trust_jonas
typestringYes—boolean | number | integer | string | enum | date | time | datetime
scopestringYes—global | chapter | page | session | persistent — see below
enumstring[] (min 1, unique)When type is "enum"—Allowed values
defaultJsonValue——Initial value. Conditionally typed: must be an integer when type is "integer", a number for "number", a boolean for "boolean"
descriptionstring——Authoring note
visibilitystring—"public"public | private. Private variables are for internal/engine use (e.g. a read-only user.age supplied by the host) and are not surfaced to reader-facing UI
readOnlyboolean—falseStory content cannot mutate this variable; its value comes from the environment

Note the stricter ID pattern: unlike a general Identifier, variable IDs are dot-separated segments (prefs.hints is valid, prefs..hints or a leading dot is not). The first segment must start with a letter or digit; underscores (trust_jonas) are allowed since 1.7.0 — a 1.6 validator rejects them. Plugin state variables use the reserved plugin. prefix — see Panels.

The five scopes

scope controls the lifetime and reset behavior of a variable's value:

ScopeLifetime
globalThe whole work, for the duration of the reading context
chapterReset when the reader enters a different chapter
pageReset when the reader moves to a different page
sessionKept for the current reading session only
persistentStored across sessions (e.g. reader preferences, unlocked routes)

How the player stores and restores persistent values is an implementation concern of the runtime — see Saving Progress and the cross-cutting Variables concept page.

Example

{
  "variables": {
    "definitions": [
      {
        "id": "prefs.hints",
        "type": "boolean",
        "default": true,
        "scope": "persistent",
        "visibility": "public",
        "description": "Reader wants hint overlays shown"
      },
      {
        "id": "path.choice",
        "type": "enum",
        "enum": ["none", "left", "right"],
        "default": "none",
        "scope": "session"
      },
      {
        "id": "user.age",
        "type": "integer",
        "default": 18,
        "scope": "global",
        "visibility": "private",
        "readOnly": true
      }
    ]
  }
}

Using variables

Read them in any JsonLogic field:

{ "visibleIf": { "var": "prefs.hints" } }

Don't define a variable to mirror the reader's speech preference: since schema 1.3, speech bubbles are implicitly subject to the speech toggle — no prefs.speech-style boilerplate needed.

{ "condition": { "==": [ { "var": "path.choice" }, "left" ] } }

Write them with mutations on edges (action) or hotspot actions (goTo.mutations, setVariables.mutations):

{
  "op": "set",
  "var": "path.choice",
  "value": "left"
}

Mutation operations are set, increment, toggle, append, and remove — documented in Graph.

Declare every variable you reference. The schema cannot verify that { "var": "path.choice" } matches a definition — an undeclared variable simply evaluates to null at runtime, which can silently disable branches.