The entire narrative of The Beach Mystery lives as a plain JavaScript object calledDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/apursley2012/the-beach-mystery/llms.txt
Use this file to discover all available pages before exploring further.
STORY_NODES in data/story.js. Each entry in that object is a node — a single unit of story that can display text, present a choice, or pose a riddle. Nodes are connected to one another through next properties and options[].next arrays, forming a directed graph you can extend at any point simply by adding new objects and updating a single reference. This guide walks you through every step needed to splice in a new branch, from identifying the right anchor point to wiring up a custom ending.
How the Node Graph Works
Every node has a unique stringid that other nodes reference to hand control forward. When the game engine finishes displaying a node, it looks up the next node ID and transitions to it. The three node types determine what players see and how control flows:
| Type | What it renders | How it advances |
|---|---|---|
narration | A sequence of text lines, then a Continue button | Follows the single next property |
choice | A prompt and two or more labelled buttons | Follows options[i].next for whichever button the player taps |
riddle | Riddle text, a text input, and an optional hint | Follows next only after the correct answer is entered |
setState— an object whose keys are written into the game state when the node is reached (e.g.{ hasJewel: true })ending— the ID of an ending declared in theENDINGSobject; reaching this node triggers the victory screen
Nodes are loaded at start-up and never fetched from a server — the full graph is in memory for the entire session. Adding a node only requires a deploy; there is no database to migrate.
Adding a New Branch
Identify where to branch
Open Your goal is to remove the direct
data/story.js and find the node that should lead into your new content. Look at its next or options[].next values to confirm which node currently follows it.For example, suppose you want to add a new scene between caveSkippedWithJewel and its ending. Currently that node ends the story immediately:ending from this node, point its next to a new node you will create, and then let the new node carry the ending reference.Add a narration node
Narration nodes are the simplest building block. Add a new entry to
STORY_NODES with a unique camelCase id, a scene key that maps to an illustration, an array of lines, and a next pointing to whatever follows your new content.Add a choice node (if branching)
To give players a decision at your new narrative beat, add a
choice node. Each entry in options needs a label (the button text), a value (an internal string), and a next (the node ID that branch leads to).Choice nodes do not have a
lines array or a next property — all flow comes through options[].next. Do not add both.Add a new ending (if needed)
Endings are declared in the Then reference it from the terminal narration node by setting
ENDINGS object near the top of data/story.js and are distinct from story nodes. Add a new key whose value contains id, name, description, and icon:ending: 'mysteriousTracks' instead of a next property.Update the STORYLINES array
STORYLINES is the list that powers the Storylines reference page and the post-game replay prompt. Add one entry for each complete path through your new branch:Wire up the preceding node
Now connect the node that currently leads somewhere else to your new branch entry-point instead. For the After:
caveSkippedWithJewel example, remove the ending property and add next:Before:Add a new scene image key (if using new artwork)
If your new node uses a scene key that does not already exist in
data/sceneImages.js, you must register it. See the Scene Artwork guide for the full process. In brief:- Drop your PNG into
assets/story-art/. - Add an entry to the
sobject indata/sceneImages.js:
- Use
'tracks'as thescenevalue in your new node.
beach-sunset, beach-walk, animal-den, etc.) can be reused across multiple nodes without side effects.Personalization Variables
Lines in any node type can include{placeholder} tokens that are substituted with the player’s answers from the opening questionnaire. The full set of available variables is:
| Variable | Source |
|---|---|
{firstName} | Player’s first name |
{favoriteColor} | Player’s favourite colour |
{favoriteAnimal} | Player’s favourite animal |
{cartoonChar} | Player’s favourite cartoon character |
{favoriteJewel} | Player’s favourite jewel |
{formatted_time} | Current time of day (formatted on render) |
lines arrays of any narration node, as in the beachOpening node ("Hello, {firstName}!") and chestDiscovery ("you glance at your {cartoonChar} watch").
Using setState Flags
A narration node can write boolean flags into game state by including a setState object. These flags can be read later by other nodes or by conditional rendering logic. For example, chestOpened sets three flags at once:
caveEnteredWithJewel vs caveEnteredWithoutJewel exist as separate nodes precisely because the jewel state determines which descriptive text is appropriate. If your new branch needs to behave differently depending on whether the player opened the chest, create two parallel node variants and route to them from the appropriate choice options.
There is no runtime conditional-lines feature inside a single node — if you need divergent prose, the idiomatic pattern is two separate nodes with the same
scene but different lines, connected by a prior choice.Preventing Broken References
The node graph is validated at read-time only — the app will silently fail to advance if anext or options[].next value references a node ID that does not exist.
A safe workflow when restructuring:
- Add the new nodes first.
- Update all
next/options[].nextreferences to point to the new IDs. - Only then remove or rename old nodes.
- Run
npm run devand walk through the affected branch manually to confirm every Continue button and every choice button reaches a valid next node.