---
name: browser-interactive-fiction
description: Build and deploy browser-based interactive fiction (text adventure) games with branching narrative, game state, animations, and deploy to GitHub Pages. Vanilla JS, no frameworks.
---

# Browser Interactive Fiction Game

Build a complete text-adventure / choose-your-own-adventure game in vanilla HTML/CSS/JS, deployable as static files.

## Architecture

### File Structure
```
game/
├── index.html      # Entry page with screens (title, game, modal)
├── style.css       # Dark nautical theme with canvas BG
├── game.js         # Engine: Game, GameState, Typewriter, Ocean
└── scenes.js       # Scene graph — all story content
```

### Core Engine Patterns

#### 1. State Object
```javascript
const GameState = {
  currentScene: 'prolog',
  flags: {},           // boolean flags for story branching
  inventory: [],       // item names
  boatHull: 100,       // 0-100
  supplies: 100,
  water: 100,
  morale: 50,
  day: 1,
  location: 'Порт Адриатика',
  weather: 'ясно',
  // Methods
  save(), load(), reset()
};
```

**Important**: Use `localStorage.setItem('game_save', JSON.stringify(GameState))` and parse on load.

#### 2. Scene Graph (scenes.js)
Each scene is an object in a `Scenes` dictionary:

```javascript
const Scenes = {
  scene_id: {
    id: 'scene_id',
    character: '👤 Name',
    text: `Story text...\n\nNew paragraph.`,
    onEnter: (state) => {
      // Modify state, set flags
      Game.effect.setFlag('flag_name');
    },
    choices: [
      { 
        text: 'Button label',
        nextScene: 'next_scene_id',
        condition: (s) => s.flags.flag_name,  // optional
        effect: (s) => { /* state changes on pick */ }
      },
    ]
  }
};
```

Scenes MUST be a `const` object on `window.Scenes` (no modules — avoids CORS issues on GitHub Pages).

#### 3. Avoid `onclick` in HTML
❌ **Don't** use `onclick="startGame()"` — requires global functions
✅ **Do** use `addEventListener` + button ids

Bind UI once after DOM ready:
```javascript
Game.init() {
  document.getElementById('btn-start').addEventListener('click', () => this.start());
}
```
Then auto-call `Game.init()` at the bottom of game.js.

#### 4. Text with Typewriter Effect
```javascript
const Typewriter = {
  start(element, text, speed, onComplete) {
    // Clear, then append char by char
  },
  skip() { /* finish instantly */ }
};
```

#### 5. UI Pattern: Screens
Use divs with `screen` class, toggle via `classList.add/remove('active')`:
- `#title-screen` — start/load buttons
- `#game-screen` — story text + choices + status bar
- Modals: `#inventory-modal`, `#save-modal`

### CSS: Dark Nautical Theme

Key techniques used:
- Full-viewport canvas behind content (fixed position)
- `.game-bg canvas#ocean-canvas` — wave animation via JS
- `.waves`, `.fog` CSS pseudo-elements for atmosphere
- Dark palette: `#0a0e1a` (deep navy), `#1a2a4a` (mid-blue), `#c9a84c` (gold accents)
- Glassmorphism modals: `backdrop-filter: blur(8px)`
- Font: `'Courier New'` for capt log, system sans-serif for UI
- Status bar: horizontal flex items with icons

### Canvas Ocean Animation
```javascript
const Ocean = {
  init() { /* get canvas, start loop */ },
  animate() {
    // Draw gradient sky, wave lines with Math.sin(time + x * freq) * amplitude
    // requestAnimationFrame(animate);
  }
};
```
Use alternating wave layers with different phases for parallax feel.

### Deploy on GitHub Pages

1. All files go in same directory (or subdirectory like `game/`)
2. Push to `master` branch (or `gh-pages`)
3. **Known issue**: GitHub Pages CDN caches aggressively. After push:
   - Wait 1-10 minutes for CDN to update
   - Use `window.location.reload(true)` or add `?v=N` to URL for testing
   - Python `urllib` may show new content before browser does
   - Push `git commit --allow-empty -m "force rebuild"` to trigger rebuild

## Pitfalls

- **CDN cache**: GitHub Pages caches by URL for custom domains. Hard refresh (`?v=N`) may not work in headless browser — use Python `urllib` to verify actual server content vs browser cache
- **Script loading order**: `scenes.js` MUST load before `game.js` (game references Scenes). Use plain `<script>` tags, NOT `type="module"` — modules create scope isolation that breaks cross-file references
- **Global scope**: `const Game`, `const Scenes`, `const GameState`, `const Typewriter`, `const Ocean` — all must be `const` (or `var`) at top level so each file can access the others' globals
- **No `onclick`**: Browser inline handlers need global functions. Use `addEventListener` in `init()` instead — cleaner, works with content-security-policy
- **Save on same origin**: `localStorage` works on GitHub Pages. For local testing, use `file://` protocol or `python3 -m http.server`
