Narrative bundle
The Narrative bundle is a short visual novel slice. The apprentice arrives at the forge, talks to Master Halden, and chooses whether to accept the lesson or practice alone first. Two endings. About 90 seconds end-to-end. Boot lands on a main menu (New Game / Continue / Settings / Credits) before the first dialogue node.
Pure DOM, no canvas, no game engine. The dialogue runner is the same shape the Top-down RPG bundle reuses for NPC chatter, so anything you build here transfers cleanly to a more complex project.
What ships
Section titled “What ships”- 14 TypeScript files across 6 scenes (main menu, settings, credits, dialogue runner, complete) plus the GameManager spine, 7 system components, and a Yarn-inspired flat dialogue node graph.
- 3 background plates (forge, courtyard, scriptorium) with scene-swap transitions via the
scenefield on dialogue nodes. - 2 characters x 4 emotions = 8 portraits (idle / engaged / concerned / determined). Selected per node via the
emotionfield. - 3 ready-made characters in the asset bank (Wren, Iolen, Kael) with all four emotions, copy them into
public/portraits/to use them. - Ambient interior music loop on the Music bus plus 3 dialogue SFX cues (dialogue-blip, choice-pick, chapter-complete) layered via AudioManager bus listeners.
- Full game shell: main menu, settings overlay (4 audio sliders + subtitle toggle), credits, complete scene.
Systems wired in
Section titled “Systems wired in”The Narrative bundle ships with 7 systems registered:
- EventBus carries dialogue advance and chapter complete events.
- ProjectConfig holds typewriter speed and similar tunables.
- StateMachine drives the menu / dialogue / settings / credits / complete flow with pushdown overlays for Settings and Credits.
- SaveSystem persists single-slot autosave to localStorage.
- AudioManager owns the bus tree with ducking and crossfade.
- SettingsManager renders the audio sliders and subtitle toggle.
- ThemeManager drives the violet-on-charcoal palette.
Controls
Section titled “Controls”| Input | Action |
|---|---|
| Click New Game / Continue | Start dialogue from the menu |
| Settings / Credits on menu | Pushdown overlays (Back returns) |
Click anywhere / Space / Enter | Advance dialogue or skip the typewriter |
| Click a choice button | Pick that branch |
R (after the ending) | Restart the chapter |
Editing dialogue
Section titled “Editing dialogue”src/data/story.dialog.json is a flat list of nodes. Each node is one of three shapes:
- Linear (
{ id, speaker, emotion, text, next }): runner walksnexton advance. - Branch (
{ id, speaker, emotion, text, choices: [{ label, target }] }): runner waits for a click. - Terminal (
{ id, speaker, emotion, text, endingId }): runner firesdialogue_chapter_completeand shifts to the ending card.
Optional fields: scene swaps the backdrop on enter (must match a src/data/scenes.ts id). emotion selects the speaker’s portrait variant (idle | engaged | concerned | determined).
Open the file in Forge and it routes to the Dialog tab. You can drag nodes, add choices, swap emotions, and save back to JSON. The visual graph keeps the same flat-list shape the runtime expects.
Adding a character
Section titled “Adding a character”Drop a portrait set under public/portraits/<id>/ with all four emotions named idle.png, engaged.png, concerned.png, determined.png. Then register in src/data/characters.ts:
{ id: 'wren', name: 'Wren', portraits: { idle: '/portraits/wren/idle.png', engaged: '/portraits/wren/engaged.png', concerned: '/portraits/wren/concerned.png', determined: '/portraits/wren/determined.png',}}Missing emotion variants fall back to idle.png silently, so you can add a new emotion on one node without committing the full set first.
If you have the agent on, ask it to generate a portrait set via forge.generate_image and chroma-key the magenta backgrounds via forge.alpha_mask_smart. See Generate art, audio, and 3D.
Adding a scene background
Section titled “Adding a scene background”Drop a 1920x1080 PNG into public/backgrounds/ and append to src/data/scenes.ts:
{ id: 'rune_shrine', background: '/backgrounds/bg_rune_shrine.png' }Reference scene: 'rune_shrine' on a node’s enter to transition. The asset bank ships 3 unused backdrops (apprentice_dormitory, rune_shrine, canyon_overlook) ready to copy in.
What’s intentionally not here
Section titled “What’s intentionally not here”- Minimal audio. AudioManager ships with bus tree, ducking, and crossfade plus an ambient interior loop and 3 dialogue SFX. Dialogue voice-over is an extension. Drop MP3s into
public/audio/voice/and key per-node viaaudio.playSfx('/audio/voice/halden-l1.mp3'). - No multi-slot save. Single
progressslot plus autosave. Multi-slot save UI is an extension. - No localization. English-only strings inline in
story.dialog.json. - No save import / export. localStorage is the entire persistence surface.