Narratomidocs
Writing

Choice

The branching beat: options, inline replies, locks, timers, interrupts, and card layouts.

A choice beat puts options on screen and stops. What each option does, it does through its own route: an edge into another scene, or a small inline body that plays and rejoins this scene. There is no separate "effects" field, because an inline body of set beats is that field.

Every choice needs at least one option. Choices cannot nest inside choices.

Beat fields

FieldTypeRequiredWhat it does
idstringyesStable beat id.
kind"choice"yesThe discriminator.
optionsarray, min 1yesSee below.
presentation"list" | "cards" | "grid"noAbsent means list.
columnsinteger 1 to 8noFixed column count for cards and grid. Absent means auto-fit.
timeout{ ms, optionId }noAbsent means the choice waits forever.
interruptbooleannoThe story plays on underneath the options.

presentation and columns are purely how the options are drawn. Routing, conditions, timers and cast picks are untouched by them.

Option fields

FieldTypeRequiredWhat it does
idstringyesStable option id. Conditions elsewhere can count how often it was taken.
textstringyesThe label. Tokens interpolate.
routeobjectyes{ type: "edge", edgeId } or { type: "inline", body }.
conditionconditionnoAbsent means always available.
whenLocked"hide" | "disable"noAbsent means hide.
castTargetcast idnoClicking that character's avatar picks this option.
propTargetprop asset idnoClicking that placed prop picks this option.
imageAssetasset idnoCard art, for cards and grid.
altTextstringnoDescription of the card image for assistive tech.

Routes

Graph edge. The option jumps to the edge's target scene, abandoning whatever the cursor was doing. Draw the edge in the flow view first, then pick it in the option's edge dropdown, which lists edges as "From scene → To scene". An edge id that no longer exists is DANGLING_CHOICE_ROUTE.

Inline body. The option plays a short body of line, stage, set and wait beats, then continues with the beat after the choice. Add rows with the "add line", "add stage", "add set" and "add wait" buttons under the option, and reorder them with the up and down arrows on each row. This is the right shape for a reply, a shrug, an item pickup: anything that does not deserve a scene.

Locking an option

A lock is a condition, not a flag. Write the condition in the option's condition editor (see Conditions); when it evaluates false, whenLocked decides what the reader sees:

  • hide, the default: the option is not rendered at all.
  • disable: the option renders greyed out with a padlock, and is announced as "Locked" to screen readers.

Disable is the honest choice when you want the reader to know something is missable. Hide is right when the option's existence is itself a spoiler.

Timers

Tick timed in the beat header and two more controls appear: the milliseconds and a dropdown of this beat's own options for the default. Both are required, which is why a timeout is one object: a clock with nothing to hand the story to has no meaning.

The reader sees a draining gauge. When it runs out, the default option is taken exactly as if it had been clicked: it records the same "taken" counter, and the story moves past the choice. A save can never rest on a lapse that has not happened yet.

One rule surprises people: if the default option is hidden or disabled when the choice comes up, there is no countdown at all. The reader keeps the choice and picks. Taking a locked option would kill the playthrough, and inventing a different one is a story decision you did not write.

Interrupts

Tick interrupt and the story does not stop. The options sit on screen while the following beats play underneath, and the reader answers or does not. An unanswered interrupt expires when play crosses into another scene, and reaching any other choice replaces it: one choice surface at a time, last one wins.

Interrupt options must route by edge. An inline body has no unambiguous rejoin point while the cursor is moving underneath it, so the compiler rejects it as INTERRUPT_INLINE_ROUTE, the timeout default included. A timeout composes normally: the countdown starts when the interrupt goes live.

Cards and grids

Switch presentation to cards (a wrapping row) or grid (a card grid over the stage), then give each option a card image. The text label always renders under the image, so a card is never an unlabeled picture. Set columns to pin the layout to a fixed width, or leave it empty for auto-fit.

An imageAsset that is in the library but is not an image is INVALID_CHOICE_IMAGE; one that is not in the library at all is MISSING_ASSET.

Clickable picks

castTarget names a cast member. While the option is live and takeable, clicking that character's staged avatar picks it. The text button stays on screen regardless, as the accessible surface and as the fallback for a character who is not currently on stage.

propTarget does the same for an object. It names a prop asset that a prop place direction has put on a mark; while the option is live, clicking that prop picks it. Same rules: the text button always renders, and a prop that is not placed, was removed, or failed to load simply has nothing to click. See Props.

A worked example

The walkthrough's beats chapter builds this in three beats: a set beat hands the reader a key they cannot see, then a choice offers an option that only exists because of it.

line    The set beat writes a variable. One just ran, and it handed you a key
        you cannot see. flag wtKey = true.
set     wtKey  =  on
choice
  1. "Open the door this option was locked behind"
       when  wtKey == true       if its condition fails: hide
       inline body:
         line  Without that set beat, this option would have been hidden or
               greyed out. Your call, per option, via whenLocked.
  2. "Ignore the door"
       inline body:
         line  Fair. But it only appeared because a set beat flipped a flag
               two lines ago.

Flip the first option's lock setting to disable and replay: the option is now visible and dead instead of absent. Delete the set beat and replay again to see what the reader sees when they never found the key.

What the reader gets

Options are numbered on screen, and the number keys 1 to 9 take them. Hidden options are not counted, so the digits always match the buttons the reader can see. Locked options keep their number and ignore the key.

Taking an option records a counter on that option's id, which is what taken(...) conditions read later. See Conditions.

On this page