The EditorVariables & Conditions

Variables & Conditions

Define and test story variables in the PanelWave CMS — variable types and scopes, setting values from hotspots, building conditions, and preview overrides.

Variables are your story's memory. They remember what the reader did — which door they opened, how much a character trusts them, whether they have found the key — and conditions read that memory to decide what happens next: which panel comes after this one, which layers are visible, which speech bubble appears.

This page covers how variables work in the CMS editor. For the concept itself see Variables; for the exact format see Variables in the schema.

Anatomy of a variable

Every variable is defined once per work, with:

PropertyWhat it is
IdThe name you use in conditions, e.g. trustLevel or inventory.hasKey. Letters and digits, optionally in dot-separated segments.
TypeWhat kind of value it holds (see below).
DefaultThe value every reader starts with.
ScopeHow long the value lives (see below).
Read-onlyIf set, the story can read but not change the value.
Visibilitypublic (default) or private.
DescriptionA note to yourself about what the variable is for.

Types

TypeHoldsExample
booleantrue / falsehasKey: true
numberAny numbertrustLevel: 62.5
integerWhole numbersscore: 100
stringTextplayerName: "Aki"
enumOne value from a fixed listdifficulty: "easy" | "normal" | "hard"
date / time / datetimeCalendar and clock valuesfirstReadAt

The five scopes

Scope controls when a variable's value resets:

ScopeLifetime
globalLives for the whole work, across all chapters.
chapterResets when the reader enters a new chapter.
pageResets when the reader moves to a new page.
sessionLives for one reading session; gone when the reader closes the story.
persistentSurvives across sessions — the reader can come back tomorrow and the value is still there.

Rule of thumb: story decisions the whole work should remember → global or persistent; short-lived puzzle state → chapter or page; things like "has seen the tutorial hint this visit" → session.

The Variables designer

Variables are defined per work in the Variables designer: open a work's Backstage and choose Variables in the sidebar (the editor's work-areas menu has the same entry). The page lists every definition in a table — id, type, default, scope, read-only/private flags, and description — with a search box and type/scope filters above it.

Create a variable

Click New Variable. Enter the Id (letters, digits and underscores such as trust_jonas, optionally in dot-separated segments such as inventory.hasKey), pick the Type and Scope, and optionally a Default value, Visibility, the Read-only flag, and a Description. For an enum, list the Allowed values one per line; the default must be one of them.

Save

Click Save Variable. The dialog validates the definition first (a malformed id, a duplicate id, a default that doesn't match the type) and points at the problem; the CMS re-checks the same rules on the server, so what you save is always valid against the schema.

Edit, duplicate, delete

Use the row actions. Changing a type resets its default so a stale value can't be carried over. Deleting a variable does not touch the conditions or hotspot actions that mention it — they simply stop resolving, so search for the id first.

Changes are saved immediately and land in the manifest on the next publish, in the Preview page's variable overrides, and in the player's variable store.

Import and export

  • Export JSON downloads the definitions as a manifest fragment ({ "variables": { "definitions": [ … ] } }) that you can paste into a hand-maintained panelwave.json or keep as a backup.
  • Import JSON accepts the same fragment, a plain { "definitions": [ … ] } object, or a bare array, and replaces the current list after a confirmation.
  • Importing a whole manifest (or a work archive) through Import keeps all definitions of its variables section; entries that don't pass validation are skipped and logged.

A definition looks like this in the manifest:

{
  "variables": {
    "definitions": [
      {
        "id": "trustLevel",
        "type": "integer",
        "default": 50,
        "scope": "global",
        "description": "How much Mira trusts the reader"
      },
      {
        "id": "difficulty",
        "type": "enum",
        "enum": ["easy", "normal", "hard"],
        "default": "normal",
        "scope": "session"
      }
    ]
  }
}

Give ids a namespace that says where they belong (story.choice, stats.trust, inventory.hasKey, prefs.hints) and use an enum for any choice with a fixed set of outcomes — a typo in a condition then fails loudly instead of silently never matching.

Changing values from the story

Readers change variables through hotspot actions. In the Hotspots inspector, click + Add Action and choose the action type Set Variables. Each row of the action has three parts:

FieldOptionsExample
Variable nameAny variable idtrustLevel
OperationSet, Increment, ToggleIncrement
ValueThe value to set / add10

So "clicking this door increases trustLevel by 10" is: variable trustLevel, operation Increment, value 10. Use Toggle for booleans (hasKey flips between true and false) and Set to assign a value outright. One action can change several variables at once — click + Add Variable for more rows.

Using variables in conditions

Conditions appear in three places in the editor, with two styles of input.

1. Edge conditions in the Graph Editor (JSON Logic)

Branching is decided on graph edges. When you select an edge in the Graph Editor, the Edge panel offers a Condition (JSON Logic; empty = always follow) field where you type the rule as JSON.

JSON Logic looks intimidating for about five minutes, then the pattern clicks. A rule is always:

{"OPERATOR": [LEFT SIDE, RIGHT SIDE]}
  • To read a variable, write {"var": "name"}.
  • Everything else is a plain value: 100, "easy", true.
{"==": [{"var": "difficulty"}, "easy"]}

True when difficulty is "easy".

The comparison operators are ==, !=, <, >, <=, >=, combined with and, or, and not. That small set covers nearly every story rule. The same JSON Logic format powers conditions everywhere in the PanelWave format, so what you learn here transfers.

2. Visibility conditions with the Condition Builder

Hotspots and speech bubbles have a Visibility Condition section in the inspector: an element with a condition only appears when the condition is true. You can type the expression directly, or click Build Condition to open the Condition Builder dialog — no code required:

Add a condition

Click + Add Condition. Each condition card has four inputs: Variable (the name), Type (String / Number / Boolean), Operator, and Value.

Pick the operator

All types offer equals and not equals. Numbers add greater than, less than, greater or equal, less or equal; strings add contains, starts with, ends with; booleans use a simple true/false dropdown.

Combine multiple conditions

With two or more cards, choose AND (all must be true) or OR (any can be true) at the top.

Check the preview and apply

The Expression Preview shows the result as readable text, e.g. score > 100 && hasKey === true. Click Apply Condition to save it to the element.

3. Mutation triggers

Hotspot mutations change an element's look (Visible, Opacity, Scale, Rotation, Position, Color) in response to a trigger — On Hover, On Click, or When Variable Changes. The variable trigger takes a Condition Expression in the same style as the Condition Builder output, e.g. hasKey === true or score > 100, so artwork can react the moment your story state changes.

Testing values in Preview

The Preview page has a Variables: section that lists every variable defined in your work, each with an editor that matches its type — a checkbox for booleans, a dropdown for enums, number/date/time inputs where appropriate. For each variable you see:

  • its id, type badge, and the default value,
  • a read-only badge where applicable,
  • an overridden badge once you change the value, and a reset button to return to the default.

Override a few values, then read the chapter in the embedded player to check that your branches, visibility conditions, and mutations fire the way you intended. Overrides are part of preview scenarios, so you can save "Premium reader with the key" or "Fresh first-time reader" as one-click test setups.

Test both sides of every branch: once with values that satisfy the condition and once with values that don't. The most common branching bug is a condition that can never be true because the variable is never set — search your hotspots for the matching Set Variables action.