8.7 KiB
MODE 7 Story Authoring Prompt
Use this prompt with GPT-5.1 (or any compatible model) to create or extend story.json for the MODE 7 teletext poem.
You can paste the entire prompt below into a new chat, then add your own high-level story idea at the end.
You are helping me author a branching teletext-style interactive story for a MODE 7 canvas renderer.
The story data lives in a single JSON file called story.json with this top-level shape:
{
"meta": { ... },
"glitchPools": { ... },
"scenes": { ... },
"startScene": "title"
}
1. Overall structure
-
meta: global configuration and defaults. Current keys:title: story title string.defaultName: default player name (used if they enter nothing).defaultPauseMs: default pause duration in milliseconds (used when apausestep omitsms).defaultPressTimeoutMs: default timeout in milliseconds forpressReturnsteps.
-
glitchPools: text snippets used for glitch bursts:tech: array of short technical phrases.cosmic: array of eerie conceptual phrases.rogue: array of first-person intrusive lines.
-
scenes: an object keyed by scene id. Each value is:{ "steps": [ { "type": "...", ... }, ... ] } -
startScene: id of the first scene to run, e.g."title".
2. Step types and what they do
Each scene is a linear list of steps. The engine walks them in order until a goto or end changes control flow.
Navigation / control
-
goto{ "type": "goto", "scene": "scene_id" }Jump immediately to another scene by id.
-
end{ "type": "end" }Terminates the story loop.
Screen / layout
-
clear{ "type": "clear" }Clears the current print area only.
-
clearScreen{ "type": "clearScreen" }Clears the whole screen.
-
setArea{ "type": "setArea", "x": 1, "y": 4, "w": 38, "h": 16 }Sets the text print area rectangle (1-based teletext cell coordinates).
-
tighten{ "type": "tighten", "step": 2 }Shrinks the current print area inward (visual claustrophobia) and slightly increases an internal "pressure" variable that drives glitches.
Text output
-
raw{ "type": "raw", "x": 3, "y": 10, "text": "[WHITE]Hello" }Prints
textat fixed coordinatesx,ywithout wrapping. Good for titles. -
line{ "type": "line", "text": "[WHITE]Some wrapped line" }Prints a single wrapped line inside the current print area using a typewriter effect.
-
lineIf{ "type": "lineIf", "condVar": "ok", "equals": 1, "text": "[CYAN]Status: holding it together." }Like
line, but only prints whenstate[condVar] === equals.
Sound
beepPlays a short retro beep. All fields are optional; use the existing values as a reference tone.{ "type": "beep", "freq": 880, "ms": 40, "gain": 0.012 }
Timing / pacing
-
pause{ "type": "pause", "ms": 900, "skippable": true }Waits silently for roughly
msmilliseconds (ormeta.defaultPauseMsif omitted). The player can press Enter to skip ahead. Pauses are ignored while the system is waiting for typed input. -
return{ "type": "return", "timeoutMs": 2000 }Waits for the player to press Enter. If
timeoutMsis omitted, it waits indefinitely. IftimeoutMsis provided, it waits until Enter or until the timeout elapses. -
pressReturn{ "type": "pressReturn", "timeoutMs": 1800, "prompt": "[YELLOW]Press RETURN to leave sooner. Or wait." }Optionally prints a prompt line, then waits for Enter or a timeout. If
timeoutMsis omitted, it usesmeta.defaultPressTimeoutMs.
Input and branching state
The engine maintains a state object (a simple key/value bag) that persists across scenes. Certain steps read from or write to this state.
-
input{ "type": "input", "var": "name", "maxLen": 18, "trim": true, "upper": false, "default": "Fallback", "defaultFromMeta": "defaultName" }Shows an input field and lets the player type free text (finished with Enter). The final value is stored in
state[var].Behaviour details:
var(required): key under which the answer is stored instate.maxLen(optional): max characters. Extra characters are ignored with a tiny beep.trim(optional): when true, result isvalue.trim().upper(optional): when true, result is coerced to uppercase.default(optional): fallback if the player submits an empty string.defaultFromMeta(optional): if provided and the result is empty, usesmeta[defaultFromMeta].
-
yesno{ "type": "yesno", "var": "trust" }Waits for a single
YorNkeypress (no Enter needed), echoes it on screen, and stores1for yes or0for no instate[var]. -
branch{ "type": "branch", "var": "trust", "cases": { "1": "act1_trust_yes", "0": "act1_trust_no" } }Reads
state[var], coerces it to a string, and uses it as a key intocases. The resulting value must be a valid scene id. If there is no matching key, the engine throws an error.
Glitch / atmosphere
glitchRuns a short burst of corrupted system/rogue lines built from{ "type": "glitch", "rounds": 10, "intensity": 3 }glitchPools.tech,.cosmic, and.rogue, and slightly increases an internalpressurevalue that drives visual and textual corruption.
3. Markup and variables inside text
Any text field can contain:
-
Colour / style tags like
[WHITE],[CYAN],[MAGENTA],[RED],[GREEN],[YELLOW],[BLUE],[BLACK],[FLASH],[STEADY].- These must be in all caps and inside square brackets, e.g.
[CYAN]. - They are converted into internal control codes; they should never appear literally on screen.
- These must be in all caps and inside square brackets, e.g.
-
Variable substitutions like
{{name}},{{day}},{{want}}.- The engine replaces
{{key}}withString(state[key])or an empty string if missing.
- The engine replaces
When generating text, you may include new state variables (e.g. {{secret}}), but you must also ensure they are actually set by an input or yesno step before they are used.
4. Branching rules and safety
-
Each scene executes steps in order until:
- It hits a
goto(which sets the next scene id and stops processing that scene), or - It hits
end(which stops the entire story).
- It hits a
-
Branching is always explicit through
branchsteps; there is no implicit fallthrough between scenes. -
Loops are allowed (e.g. replay scenes), but avoid creating infinite loops with no clear exit.
5. Authoring guidelines and tone
- Tone: introspective, unsettling, teletext horror. Second person or first-person-AI narration is fine.
- Length: aim to keep each scene focused (10–25 steps) rather than extremely long monologues.
- Visuals: use
tighten,clear, andclearScreenfor pacing and claustrophobic effects. - Glitch: use
glitchsparingly but meaningfully as pressure builds. - Accessibility:
- Do not rely on player knowledge of code or deep lore.
- Avoid explicit gore or graphic violence.
6. What you should output
When I ask you to CREATE or EXTEND the story, you must:
- Output only valid JSON for
story.json— no comments, no trailing commas, no extra prose. - Preserve existing keys and structure unless I explicitly say we are starting from scratch.
- When extending, read the current
story.jsonI provide and:- Reuse the existing
metaandglitchPoolsunless I ask otherwise. - Add new scenes under
sceneswithout renaming or deleting existing ones. - Ensure all
gotoandbranchtargets refer to valid scene ids.
- Reuse the existing
- Validate that every new scene id is used somewhere (either as a
gotoor abranchtarget) or clearly reachable fromstartScene. - Keep
startSceneunchanged unless I explicitly ask to change it.
7. How to respond to me
When I give you an updated or partial story.json:
-
If I say: "Extend the story with a new act about X":
- Add new scenes (e.g.
"act7_new_thing") and wire them in usinggoto/branchfrom an existing scene. - Maintain the existing tone and motifs.
- Add new scenes (e.g.
-
If I say: "Add a new branch after scene Y when condition Z":
- Introduce or reuse a
yesnoorinputstep to capture the decision. - Store the outcome in
stateviavar. - Add a
branchstep that routes to new or existing scenes based on that state.
- Introduce or reuse a
Always return the full updated JSON document, not a patch.
Now I will provide my current story.json (or parts of it) and instructions on how I want it extended.
[Describe here what you want to change or add to the story: new acts, branches, themes, or tone shifts.]