Story syntax and command reference
The 23 built-in authoring commands, their arguments, types, and implementation boundaries.
This is the .pxstory author compiler interface, not a list of every internal C++ VM command or JavaScript method. It describes the pinned 0.2.0 source snapshot.
Line-level syntax
| Syntax | Meaning |
|---|---|
; comment | Whole-line comment, not // |
*start | Label declaration |
@guide | Speaker for subsequent text |
| Ordinary text | Narration without an active speaker; dialogue otherwise |
| Blank line | End the current speaker scope |
[command ...] | One command per line |
Use UTF-8 without a BOM. The parser currently limits a Story file to 16 MiB. Labels, aliases, and explicit IDs should use stable identifiers beginning with a letter or digit, followed by letters, digits, ., _, or -, without spaces.
Named arguments use name=value. Quote strings containing spaces. Write booleans as true and false. Only add units to values when the field explicitly supports units. Structured extension arguments must be valid JSON:
[tutorial.note message="A message with spaces"]
; Syntax illustration only: declare a matching extension command first
[demo.position point=[20,40] enabled=true]There is no general instruction here to execute JavaScript inside dialogue. Do not assume ${score}, React components, or another engine's interpolation syntax works in Story text.
Identity, flow, and waiting
| Command | Common form | Notes |
|---|---|---|
id | [id main.line01] | Pin the next operation; also accepts value=... |
choice | [choice text="Go" goto=next] | Requires text and a valid local label |
choice.wait | [choice.wait] | Currently emits no separate IR operation; keep choices adjacent |
jump | [jump target=next] | Unconditional jump to a local label |
call | [call target=common] | Enter a shared passage, paired with return |
return | [return] | Resume at the caller |
end | [end] | End the Story flow |
wait | [wait duration=500] | Wait 500 milliseconds; positional duration is also accepted |
The native adapter accepts unitless milliseconds, 500ms, or 0.5s for wait. This handbook prefers integer milliseconds to avoid confusing them with JavaScript APIs that use seconds. Use finite, nonnegative values. [wait 0.5] does not mean half a second.
Use goto for choices and target for jumps/calls as the canonical readable forms. Choice text is not a label. Unknown targets produce diagnostics; a filename is not a way to bypass local-label validation.
Backgrounds and characters
| Command | Common form | Author arguments |
|---|---|---|
bg | [bg Assets/window.png] | Positional resource or asset |
show | [show guide expression=smile position=left] | character or first positional argument, expression, position, x, y, scale |
move | [move guide x=80 duration=400 wait=true] | Character, position, x, y, scale, duration, ease, wait |
hide | [hide guide] | Character or character |
Positions accept left, center, right, or 1, 2, 3; the compiler also accepts centre. Characters and expressions must resolve to registered content. show does not declare a new character.
move needs at least one of position/x/y/scale. Duration uses milliseconds. wait=true delays following Story execution; otherwise subsequent dialogue may begin immediately. Changing a position slot is not itself a promise of a smooth slide. For explicit interpolation, use x/y/scale or the animation API and inspect the result. The native transform tween defaults to 600 milliseconds and outCubic; explicit values are easier to read.
Variables and conditions
| Command | Common form | Notes |
|---|---|---|
set | [set score=1] | Assign one literal; also accepts name=score value=1 |
if | [if score >= 1] | A parsed condition expression, not JavaScript |
else | [else] | Optional alternative branch |
endif | [endif] | Close the corresponding condition |
Declare variables and types in the game catalog first. [set score=score+1] is not arithmetic assignment; use an extension when you need to increment a value. Conditions support parentheses, comparisons, arithmetic, and boolean operators. See Variables and control flow.
Audio, UI, and presentation
| Command | Common form | Actual boundary |
|---|---|---|
voice | [voice Assets/greeting.wav] | Voice resource; only the asset reference is lowered |
bgm | [bgm Assets/evening.wav] | Background music resource; asset only |
se | [se Assets/bell.wav] | Sound effect resource; asset only |
timeline | [timeline Animations/greeting.pxtimeline] | Positional reference or timeline |
ui | [ui route=save operation=push] | Route and operation; operation defaults to push |
effect | [effect value=PRESET] | One preset string, not arbitrary shader parameters |
camera | [camera value=PRESET] | One preset string, not an x/y/zoom object |
PRESET is a placeholder, not an effect bundled with the starter. Without a verified preset, use typed APIs such as ctx.stage.camera(...), ctx.stage.screenEffect(...), or a .pxeffect rather than guessing a string and assuming it plays.
Story audio commands do not automatically forward extra volume or fade arguments to the runtime. timeline is also not a complete JavaScript playback handle. An argument being tokenized does not mean the command uses it: not every built-in command strictly rejects unused arguments in this snapshot.
Extension commands and diagnostics
All other names must be declared in a project-registered .pxextension and implemented by its JavaScript entry script. An internal VM command is not automatically a public authoring command. In particular, do not assume [video], [anim], or [nvl] are undocumented universal built-ins.
PXSTORY1199 means an unknown command. Extension required arguments, types, enums, numeric ranges, and unknown parameters use PXSTORY1201–1206. Invalid, unconsumed, duplicated, or dangling explicit IDs use PXSTORY1210–1213. Fix the earliest meaningful diagnostic and validate again.
Sources: Story compiler, Runtime IR adapter, Native VM.