Skip to content

Narrative Interludes

Interludes are full-screen text scenes (chapter cards, dream sequences, journal entries read aloud), like the parchment screens between chapters in Infinity Engine Games like Baldur’s Gate. They show a background image with scrolling narrative text. The player can read at their own pace and skip when ready.

Create a YAML file in content/interludes/:

content/interludes/chapter_one.yaml
id: chapter_one
background: /assets/images/banners/dusk_road.jpg
text: |
Chapter One: A New Beginning
The road behind you stretches long and empty.
Ahead, the lights of town flicker through the evening mist.
You have heard the rumours. Strange things happening.
People going missing. Shadows that move wrong.
Someone has to look into it.
It might as well be you.
FieldRequiredDescription
idYesUnique identifier
backgroundYesBackground image path
textYesThe narrative text (plain text or @localization.key)
bannerNoOptional decorative frame/border image overlaid on the background
musicNoMusic track to play during the interlude
voiceNoNarration audio file
soundsNoArray of ambient sound filenames
scrollNoWhether text auto-scrolls upward (default: true)
scrollSpeedNoAuto-scroll speed in elisas per second (default: 30)
triggerLocationNoLocation ID where this auto-triggers on enter
triggerConditionsNoConditions that must pass for auto-trigger
effectsNoEffects applied when the interlude triggers (typically setFlag to prevent repeats)

Use INTERLUDE <id> in any dialogue node:

NODE find_letter
NARRATOR: You open the envelope with trembling hands.
SET flag foundLetter
INTERLUDE discovery_scene
END dialogue

Set triggerLocation, triggerConditions, and effects in the YAML. The effects field runs immediately when the interlude triggers. Use it to set a “seen” flag so the interlude doesn’t repeat on return visits:

id: chapter_two
background: /assets/images/banners/forest.jpg
text: |
Chapter Two: Into the Woods
The forest is older than the town.
Older than the people who named it.
triggerLocation: dark_forest
triggerConditions:
- type: hasFlag
flag: leftTavern
- type: notFlag
flag: seenChapterTwo
effects:
- type: setFlag
flag: seenChapterTwo

The effects run at trigger time (before the player even sees the interlude), so notFlag seenChapterTwo will fail if the player returns. The interlude won’t show again.

Important: do NOT set the “seen” flag in a dialogue node that fires before the interlude check. The engine evaluates triggerConditions first, then applies effects. Setting the flag in a dialogue would mark the interlude as seen before the check runs, causing it to never trigger.

ActionEffect
Click anywhereSkip (dismiss)
Skip >> buttonDismiss
Space or EnterDismiss
EscapeDismiss
Mouse wheelManual scroll (pauses auto-scroll)
↑ / ↓ arrow keysManual scroll (pauses auto-scroll)

Use a localization key for multi-language support:

id: chapter_one
background: /assets/images/banners/dusk_road.jpg
text: '@chapter.one.intro'

Then in content/locales/en.yaml:

chapter.one.intro: |
Chapter One: A New Beginning
The road behind you stretches long and empty.
...

If you’re building a custom renderer (not using GameShell), render the interlude yourself:

import { Interlude } from '@doodle-engine/react';
function MyRenderer() {
const { snapshot, actions } = useGame();
if (snapshot.pendingInterlude) {
return (
<Interlude
interlude={snapshot.pendingInterlude}
onDismiss={actions.dismissInterlude}
/>
);
}
return <div>...</div>;
}

snapshot.pendingInterlude is null when no interlude is pending. The GameRenderer and GameShell handle this automatically.