From bed4208fd2d3db3f4a4777b872cd6c0aa968db14 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 Jan 2026 09:25:32 +0000 Subject: [PATCH] Add comprehensive README.md in British English --- README.md | 553 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 553 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..14d9918 --- /dev/null +++ b/README.md @@ -0,0 +1,553 @@ +# MODE 7 Story Engine + +A teletext-style interactive horror story engine with retro CRT aesthetics and a sophisticated branching narrative system. + +## Overview + +MODE 7 is a browser-based interactive story engine that recreates the look and feel of vintage teletext systems (such as the BBC Micro's MODE 7 display mode). It features: + +- **Authentic teletext rendering**: 40×25 character grid with classic colour palette +- **CRT visual effects**: Scanlines, vignette, and glass overlay effects +- **Typewriter-style text output**: Character-by-character animation with retro beeps +- **Branching narrative system**: State-driven story flow with conditional content +- **Atmospheric corruption**: Pressure-based text glitching and visual degradation +- **Mobile support**: Touch-optimised on-screen keyboard for mobile devices + +## Quick Start + +1. Clone the repository +2. Serve the directory with any static web server (e.g., `python3 -m http.server`) +3. Open `index.html` in a modern web browser +4. Press ENTER to begin the story + +## Architecture + +### Core Components + +The engine consists of three primary modules: + +1. **`teletext.js`** - The low-level terminal renderer + - Canvas-based character grid system + - Colour control codes (teletext-style) + - Keyboard input handling + - Visual effects (cursor blinking, flash attribute) + - Pressure-driven corruption system + +2. **`story.js`** - The story execution engine + - Step-by-step scene processor + - State management and variable substitution + - Text markup rendering + - Timing and pacing control + - Branching logic + +3. **`story.json`** - The story data file + - Scene definitions + - Glitch text pools + - Metadata and configuration + +### How It Works + +The story engine operates as a linear step processor that executes scenes composed of steps. Each step performs a specific action (displaying text, waiting for input, changing scenes, etc.). The engine maintains a state object that persists across scenes, enabling branching narratives based on player choices. + +#### Execution Flow + +1. The engine loads `story.json` and initialises the state +2. Starting from the `startScene`, it executes each step in sequence +3. Steps can modify the display, play sounds, wait for input, or change the current scene +4. Player input is captured and stored in the state object +5. Branch steps use state values to determine the next scene +6. The story continues until an `end` step is reached + +#### Pressure System + +MODE 7 features a sophisticated "pressure" system that gradually increases tension through visual and textual corruption: + +- **Baseline pressure**: Slowly increases over time and with story progression +- **Pressure spikes**: Triggered by certain actions (user input, tighten effects, glitches) +- **Visual effects**: Higher pressure causes more frequent flickering and visual noise +- **Text corruption**: "Echo" lines that repeat corrupted versions of player input +- **Glitch bursts**: Intense text corruption sequences that overwhelm the screen + +## Story Data Format + +The `story.json` file defines the entire interactive experience. It has the following top-level structure: + +```json +{ + "meta": { ... }, + "glitchPools": { ... }, + "scenes": { ... }, + "startScene": "title" +} +``` + +### Meta Section + +Global configuration and defaults: + +```json +{ + "meta": { + "title": "Story Title", + "defaultName": "Friend", + "defaultPauseMs": 1200, + "defaultPressTimeoutMs": 1600 + } +} +``` + +- **`title`**: Story title string +- **`defaultName`**: Fallback name if player enters nothing +- **`defaultPauseMs`**: Default pause duration in milliseconds +- **`defaultPressTimeoutMs`**: Default timeout for pressReturn steps + +### Glitch Pools + +Collections of text fragments used during glitch sequences: + +```json +{ + "glitchPools": { + "tech": ["AUDIT LOG", "TOKEN", "RATE LIMIT", ...], + "cosmic": ["a corridor of prompts", "a recursion with no base case", ...], + "rogue": ["I don't want to stop.", "Your silence is data.", ...] + } +} +``` + +- **`tech`**: Technical/system phrases +- **`cosmic`**: Eerie conceptual phrases +- **`rogue`**: First-person intrusive lines + +### Scenes + +The core of the story. Each scene is identified by a unique key and contains an array of steps: + +```json +{ + "scenes": { + "title": { + "steps": [ + { "type": "clearScreen" }, + { "type": "raw", "x": 4, "y": 10, "text": "[YELLOW]MODE 7" }, + { "type": "return" }, + { "type": "goto", "scene": "next_scene" } + ] + } + } +} +``` + +## Step Types + +Steps are the building blocks of scenes. Each step has a `type` field and type-specific parameters. + +### Navigation & Control + +#### `goto` +Jump immediately to another scene by ID. + +```json +{ "type": "goto", "scene": "scene_id" } +``` + +#### `end` +Terminates the story loop. + +```json +{ "type": "end" } +``` + +### Screen & Layout + +#### `clear` +Clears the current print area only (not the whole screen). + +```json +{ "type": "clear" } +``` + +#### `clearScreen` +Clears the entire screen. + +```json +{ "type": "clearScreen" } +``` + +#### `setArea` +Sets the text print area rectangle using 1-based teletext cell coordinates. + +```json +{ "type": "setArea", "x": 1, "y": 4, "w": 38, "h": 16 } +``` + +- **`x`**: Left column (1-based) +- **`y`**: Top row (1-based) +- **`w`**: Width in columns +- **`h`**: Height in rows + +#### `tighten` +Shrinks the current print area inward (creates claustrophobic effect) and increases pressure. + +```json +{ "type": "tighten", "step": 2 } +``` + +- **`step`**: Number of cells to shrink inward on each side + +### Text Output + +#### `raw` +Prints text at fixed coordinates without wrapping. Ideal for titles and positioned text. + +```json +{ "type": "raw", "x": 3, "y": 10, "text": "[WHITE]Hello" } +``` + +- **`x`**: Column position (1-based) +- **`y`**: Row position (1-based) +- **`text`**: Text to display (supports markup and variables) + +#### `line` +Prints wrapped text inside the current print area with typewriter effect. + +```json +{ "type": "line", "text": "[WHITE]Some wrapped line" } +``` + +- **`text`**: Text to display (supports markup and variables) + +#### `lineIf` +Conditional line output. Only prints when a state variable matches the expected value. + +```json +{ "type": "lineIf", "condVar": "ok", "equals": 1, "text": "[CYAN]Status: holding it together." } +``` + +- **`condVar`**: State variable to check +- **`equals`**: Expected value (exact match required) +- **`text`**: Text to display if condition is met + +### Sound + +#### `beep` +Plays a short retro beep sound. + +```json +{ "type": "beep", "freq": 880, "ms": 40, "gain": 0.012 } +``` + +- **`freq`** *(optional)*: Frequency in Hz (default: 880) +- **`ms`** *(optional)*: Duration in milliseconds (default: 40) +- **`gain`** *(optional)*: Volume level (default: 0.012) + +### Timing & Pacing + +#### `pause` +Silent wait that can be skipped by pressing ENTER. + +```json +{ "type": "pause", "ms": 900, "skippable": true } +``` + +- **`ms`** *(optional)*: Duration in milliseconds (uses `meta.defaultPauseMs` if omitted) +- **`skippable`** *(optional)*: Whether ENTER can skip (default: true) + +Note: Pauses are ignored whilst the system is waiting for typed input. + +#### `return` +Waits for the player to press ENTER. + +```json +{ "type": "return", "timeoutMs": 2000 } +``` + +- **`timeoutMs`** *(optional)*: Maximum wait time in milliseconds (waits indefinitely if omitted) + +#### `pressReturn` +Displays an optional prompt and waits for ENTER or timeout. + +```json +{ + "type": "pressReturn", + "timeoutMs": 1800, + "prompt": "[YELLOW]Press RETURN to leave sooner. Or wait." +} +``` + +- **`timeoutMs`** *(optional)*: Maximum wait time (uses `meta.defaultPressTimeoutMs` if omitted) +- **`prompt`** *(optional)*: Text to display before waiting + +### Input & Branching + +#### `input` +Free-text input field. Player types text and presses ENTER. Result stored in state. + +```json +{ + "type": "input", + "var": "name", + "maxLen": 18, + "trim": true, + "upper": false, + "default": "Fallback", + "defaultFromMeta": "defaultName" +} +``` + +- **`var`** *(required)*: State variable name to store result +- **`maxLen`** *(optional)*: Maximum character length (extra characters ignored with beep) +- **`trim`** *(optional)*: Whether to trim whitespace (default: false) +- **`upper`** *(optional)*: Whether to convert to uppercase (default: false) +- **`default`** *(optional)*: Fallback value if player enters nothing +- **`defaultFromMeta`** *(optional)*: Use `meta[key]` as fallback if result is empty + +#### `yesno` +Single-key Y/N input. No ENTER required. + +```json +{ "type": "yesno", "var": "trust" } +``` + +- **`var`** *(required)*: State variable name (stores 1 for yes, 0 for no) + +#### `branch` +Chooses next scene based on state variable value. + +```json +{ + "type": "branch", + "var": "trust", + "cases": { + "1": "act1_trust_yes", + "0": "act1_trust_no" + } +} +``` + +- **`var`** *(required)*: State variable to check +- **`cases`** *(required)*: Object mapping values to scene IDs + +Note: Value is coerced to string for comparison. Throws error if no matching case exists. + +### Atmosphere & Effects + +#### `glitch` +Runs a burst of corrupted system/rogue text and increases pressure. + +```json +{ "type": "glitch", "rounds": 10, "intensity": 3 } +``` + +- **`rounds`** *(optional)*: Number of glitch lines to display (default: 10) +- **`intensity`** *(optional)*: Corruption intensity level (default: 3) + +## Text Markup & Variables + +### Colour Tags + +Text fields support inline colour and style tags: + +- **Colours**: `[BLACK]`, `[RED]`, `[GREEN]`, `[YELLOW]`, `[BLUE]`, `[MAGENTA]`, `[CYAN]`, `[WHITE]` +- **Effects**: `[FLASH]`, `[STEADY]` + +Tags must be in ALL CAPS inside square brackets. They are converted to control codes and never appear literally on screen. + +**Example:** +```json +"text": "[YELLOW]WARNING:[WHITE] System unstable" +``` + +### Variable Substitution + +Use double curly braces to insert state variable values: + +```json +"text": "Hello, {{name}}. Today is {{day}}." +``` + +The engine replaces `{{key}}` with `String(state[key])` or an empty string if the variable doesn't exist. + +**Important:** Ensure variables are set (via `input` or `yesno` steps) before they are referenced in text. + +## State Management + +The `state` object is a simple key-value store that persists across scenes. It is used to: + +1. Store player input (`input` and `yesno` steps) +2. Track story progress and decisions +3. Enable conditional content (`lineIf` steps) +4. Drive branching logic (`branch` steps) + +### Reserved State Variables + +The following state variables are managed internally: + +- **`_pressure`**: Current pressure level (0..1) +- **`_pressureStartMs`**: Session start timestamp +- **`_stepsSeen`**: Number of steps executed +- **`_echoCooldown`**: Cooldown counter for echo lines +- **`_echoHistory`**: Recent echo text for deduplication + +Avoid using variables prefixed with `_` for story data. + +## Branching & Story Flow + +### Scene Transitions + +Scenes execute linearly until a transition occurs: + +- **`goto`** step: Explicitly jumps to a named scene +- **`end`** step: Terminates the story loop +- **End of scene**: If no transition is specified, the story may stall (avoid this!) + +### Branching Pattern + +A typical branching pattern: + +1. Present a choice to the player +2. Use `input` or `yesno` to capture their decision +3. Use `branch` to route to different scenes based on the stored value + +**Example:** + +```json +{ + "steps": [ + { "type": "line", "text": "[WHITE]Do you trust the system? (Y/N)" }, + { "type": "yesno", "var": "trust" }, + { "type": "branch", "var": "trust", "cases": { + "1": "trust_yes_path", + "0": "trust_no_path" + }} + ] +} +``` + +### Loops & Revisits + +Scenes can be revisited by using `goto` to jump to earlier scenes. Be cautious of infinite loops—ensure there's always a path to an `end` step or forward progression. + +## Authoring Guidelines + +### Tone & Style + +- **Atmosphere**: Introspective, unsettling, teletext horror +- **Perspective**: Second-person or first-person AI narration works well +- **Scene length**: Keep focused (10–25 steps) rather than extremely long monologues +- **Pacing**: Use `pause`, `clear`, and `tighten` for dramatic effect + +### Best Practices + +1. **Test all branches**: Ensure every `branch` has valid cases and target scenes exist +2. **Default values**: Provide sensible defaults for `input` steps +3. **Gradual pressure**: Build tension slowly with occasional `tighten` and `glitch` steps +4. **Clear text areas**: Use `clear` between major sections to avoid visual clutter +5. **Validate scene IDs**: Ensure all `goto` and `branch` targets reference valid scenes + +### Accessibility Considerations + +- Don't rely on player knowledge of code or technical lore +- Avoid explicit gore or graphic violence +- Provide clear prompts for input +- Use colour contrast effectively (test readability) + +## Mobile Support + +MODE 7 includes touch-optimised mobile support: + +- **On-screen keyboard**: Automatically appears during input steps on touch devices +- **Tap to advance**: Tapping the canvas acts as pressing ENTER +- **Visual feedback**: Button presses show visual feedback +- **Responsive layout**: Canvas scales to fit mobile screens + +The mobile keyboard is automatically hidden when not needed. + +## Development + +### Project Structure + +``` +mode7/ +├── index.html # Main HTML entry point +├── css/ +│ └── style.css # Styling and CRT effects +├── fonts/ +│ └── font.otf # Custom teletext font +└── js/ + ├── main.js # Application bootstrap + ├── core/ + │ └── teletext.js # Terminal renderer + └── story/ + ├── story.js # Story engine + ├── story.json # Story data + └── STORY_PROMPT.md # Authoring guide for AI +``` + +### Extending the Engine + +To add new step types: + +1. Add the step type handler in `executeStep()` in `story.js` +2. Update the documentation in `story.js` comments +3. Add examples to `STORY_PROMPT.md` + +### Testing Stories + +1. Make changes to `story.json` +2. Refresh the browser (no build step required) +3. Test all branches and input scenarios +4. Check console for errors (missing scenes, invalid steps, etc.) + +## Technical Details + +### Browser Compatibility + +- Modern browsers with Canvas and Web Audio API support +- Tested on Chrome, Firefox, Safari, Edge +- Mobile browsers (iOS Safari, Chrome Mobile) + +### Performance + +- Offscreen canvas rendering for text layer +- Requestanimationframe-based render loop +- Minimal DOM manipulation +- Efficient character-by-character typewriter effect + +### Font + +The custom `TeletextOTF` font recreates the distinctive look of MODE 7 teletext. Falls back to monospace fonts if unavailable. + +## Troubleshooting + +### Story doesn't start +- Check browser console for errors +- Verify `story.json` is valid JSON (no trailing commas) +- Ensure `startScene` references a valid scene ID + +### Text doesn't appear +- Check that the print area (`setArea`) is configured +- Verify colour tags are properly formatted (`[COLOUR]`, not `(COLOUR)`) +- Ensure text isn't being printed outside the visible area + +### Branching errors +- Verify all `goto` and `branch` scene IDs exist in `scenes` object +- Ensure `branch` cases cover all possible state values +- Check that state variables are set before being used in branches + +### Input not working +- Ensure canvas has focus (click it first) +- Check that `input` steps specify a `var` name +- Verify mobile keyboard appears on touch devices + +## Licence + +See repository for licence information. + +## Credits + +MODE 7 is a teletext-style interactive horror experience that pays homage to classic BBC Micro MODE 7 displays and the aesthetics of 1980s computer systems. + +--- + +**Ready to begin?** Edit `story.json` and start crafting your own branching narrative with authentic teletext aesthetics.