Skip to content

docs: Add live demos and update documentation content#17

Open
canerdogan wants to merge 6 commits intomainfrom
docs/live-demos-and-content-updates
Open

docs: Add live demos and update documentation content#17
canerdogan wants to merge 6 commits intomainfrom
docs/live-demos-and-content-updates

Conversation

@canerdogan
Copy link
Contributor

Summary

  • 12 new interactive demo HTML files — self-contained games/examples embedded in docs pages
  • 24 doc pages updated — LiveDemo components added after page titles, content improvements
  • CLAUDE.md — added demo placement rule for consistent future documentation

New demos

Demo Description
hello-world Minimal GameByte setup
architecture Service container visualization
config-playground Interactive config options
2048 Classic sliding puzzle
cookie-clicker Idle/clicker game
gem-collector Touch-based collection game
neon-pong Retro pong with neon effects
quiz-game Trivia quiz
rps-game Rock-paper-scissors
simon-says Memory pattern game
slingshot-game Physics-based slingshot
whack-a-mole Tap timing game

Updated docs sections

  • Getting Started (installation, quick-start, tutorial)
  • Core Concepts (architecture, configuration)
  • Rendering (overview, 3D, hybrid mode)
  • Input, Physics, Layout, UI Components, Audio, Assets
  • Boilerplate screens, Changelog v1.3.0

Test plan

  • npm run docs:build succeeds
  • Each demo loads and runs in browser
  • LiveDemo components render correctly on doc pages

🤖 Generated with Claude Code

- Add 12 new interactive demo HTML files (2048, cookie-clicker, gem-collector,
  neon-pong, quiz-game, rps-game, simon-says, slingshot, whack-a-mole, etc.)
- Update 24 documentation pages with LiveDemo components placed after title
- Add demo placement rule to CLAUDE.md for consistent future docs
- Enhance changelog, architecture, configuration, and getting-started pages
- Improve rendering, physics, input, layout, and UI component docs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add esc() helper to sanitize DOM values before interpolating into
innerHTML template string. Prevents potential XSS via DOM manipulation
of range/select input values.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

// GameByte game instance and factory
let game;
const factory = graphics();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical Bug: GraphicsEngine not initialized

The graphics() function is called at top-level script scope (line 65) before the game engine is initialized. This will throw an immediate error when the page loads:

Error: GraphicsEngine not initialized. Call GraphicsEngine.initialize(renderingMode) first.

Why this happens:

  • Line 65: const factory = graphics(); executes synchronously when the script loads
  • Line 478: initApp() runs later, which calls createGame()
  • createGame() initializes GraphicsEngine, but that hasn't happened yet when line 65 executes

Fix: Move the factory initialization inside initApp() after createGame():

async function initApp() {
    await loadFrameworkFont();
    
    game = await createGame({
        container: '#game-container',
        width: WIDTH,
        height: HEIGHT,
        mode: '2d',
        backgroundColor: 0x0f172a
    });
    
    const factory = graphics(); // ✅ Now safe to call
    // ... rest of initialization
}

See GraphicsEngine.ts:148-150 and GraphicsEngine.ts:89-96

if (gameState !== 'playing') return;

const isBomb = Math.random() < 0.15;
const gem = factory.createContainer();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical Bug: GameTopBar API mismatch

This demo (and 5 others) use non-existent methods and config properties on GameTopBar.

The actual GameTopBar API (GameTopBar.ts:35-42):

Config interface:

export interface GameTopBarConfig {
  width: number;          // REQUIRED
  height?: number;
  padding?: number;
  resources?: ResourceItemConfig[];
  showSettings?: boolean;
  onSettingsClick?: () => void;
}

Public methods:

  • updateResource(type: ResourceType, value: number, animate?: boolean)
  • setPosition(x: number, y: number)
  • getContainer()
  • destroy()

Issues in this file:

  1. ❌ Constructor missing required width parameter
  2. ❌ Constructor passes non-existent properties: title, showLives
  3. ❌ Calls non-existent methods: topBar.setScore(), topBar.setCustomValue()

Other affected demos:

  • 2048-demo.html: Calls topBar.updateScore()
  • cookie-clicker-demo.html: Calls topBar.updateTitle()
  • neon-pong-demo.html: Calls topBar.updateLeftText(), topBar.updateRightText()
  • simon-says-demo.html: Calls topBar.updateLevel()
  • whack-a-mole-demo.html: Calls topBar.updateScore()

All of these method calls will throw TypeError: topBar.xxx is not a function at runtime.

Fix: Use the actual API with updateResource():

const topBar = new GameTopBar({
    width: WIDTH,
    resources: [
        { type: 'score', value: 0, label: 'Score' },
        { type: 'lives', value: 3, label: 'Lives' }
    ]
});

// Update score
topBar.updateResource('score', score, true);

// Update lives
topBar.updateResource('lives', lives, true);

Comment on lines 127 to 129
height: 60
});
playBtn.x = 150;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Position set on EventEmitter instead of container

Setting x and y directly on the GameStyleButton instance doesn't work. The button will appear at position (0, 0) instead of (150, 350).

Why: GameStyleButton extends EventEmitter (GameStyleButton.ts:66). EventEmitter doesn't have display properties like x and y. Setting these properties creates arbitrary fields on the EventEmitter object, but doesn't affect the internal display container.

Fix: Use setPosition() or position the container directly:

Suggested change
height: 60
});
playBtn.x = 150;
const playBtn = new GameStyleButton({
text: 'PLAY',
width: 200,
height: 60
});
playBtn.setPosition(150, 350);
playBtn.on('click', startGame);

Or:

playBtn.getContainer().x = 150;
playBtn.getContainer().y = 350;

Comment on lines 297 to 299
hole.container = moleContainer;
hole.type = moleType;
hole.timer = moleType.duration / difficultyMultiplier;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Timer unit mismatch (milliseconds vs seconds)

Mole timers are set in milliseconds but decremented in seconds, causing a ~1000x timing error. A mole intended to appear for 1.2 seconds will actually persist for ~20 minutes.

The issue:

Line 55-57: Durations defined in milliseconds:

const MoleType = {
    NORMAL: { color: 0x8B4513, points: 10, duration: 1200 },  // 1200ms = 1.2s
    GOLDEN: { color: 0xFFD700, points: 50, duration: 1500 },  // 1500ms = 1.5s
    ANGRY: { color: 0xDC143C, points: 25, duration: 800 }     // 800ms = 0.8s
};

Line 299: Timer set to millisecond value:

hole.timer = moleType.duration / difficultyMultiplier;  // e.g., 1200

Line 422: deltaTime is in seconds:

const deltaTime = deltaMS / 1000;  // ~0.016 at 60fps

Line 453: Timer decremented in seconds:

hole.timer -= deltaTime;  // 1200 - 0.016 = 1199.984

Result: It takes 1200 / 0.016 = 75,000 frames (~1,250 seconds) for a NORMAL mole to disappear, instead of the intended 1.2 seconds.

Fix: Convert durations to seconds when setting the timer:

Suggested change
hole.container = moleContainer;
hole.type = moleType;
hole.timer = moleType.duration / difficultyMultiplier;
const moleType = getRandomMoleType();
hole.moleType = moleType;
hole.timer = moleType.duration / 1000 / difficultyMultiplier; // Convert ms to seconds
hole.health = hole.moleType === MoleType.ANGRY ? 2 : 1;

Comment on lines 119 to 127
checks.forEach((item, i) => {
const t = factory.createText(`${item.done ? '\u2705' : '\u274C'} ${item.text}`, {
fontSize: 12,
fill: item.done ? 0x4ade80 : 0xef4444
});
t.x = 20;
t.y = 300 + i * 0;
// Stack at bottom-left
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Checklist items invisible and overlapping

The installation verification checklist has two bugs:

  1. Items never added to stage: The text elements are created but never appended with stage.addChild(t), so they'll never be visible.

  2. Zero vertical spacing: i * 0 always equals 0, so all items will be at the same y-position (300) and overlap.

Fix:

Suggested change
checks.forEach((item, i) => {
const t = factory.createText(`${item.done ? '\u2705' : '\u274C'} ${item.text}`, {
fontSize: 12,
fill: item.done ? 0x4ade80 : 0xef4444
});
t.x = 20;
t.y = 300 + i * 0;
// Stack at bottom-left
});
checks.forEach((check, i) => {
const t = factory.createText(`✓ ${check}`, {
fontSize: 16,
fill: 0x4CAF50,
fontFamily: 'Lilita One, sans-serif'
});
t.x = 30;
t.y = 300 + i * 18; // 18px spacing between items
stage.addChild(t);
});

## Live Demo
## Live Demos

import LiveDemo from '@site/src/components/LiveDemo';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CLAUDE.md violation: Import placement

The import LiveDemo statement should appear at the top of the file (after frontmatter, before the main title), not after a section heading.

CLAUDE.md rule (CLAUDE.md:320-321):

  • import LiveDemo goes after frontmatter, before the # Title

Current structure:

---
frontmatter
---

# GameByte Framework

[content...]

## Live Demos         <-- Section heading

import LiveDemo       <-- Import here ❌

Expected structure:

---
frontmatter
---

import LiveDemo       <-- Import here ✅

# GameByte Framework

[content...]

<div id="game-container"></div>
<div class="info">Arrow keys or swipe to slide tiles. Merge equal numbers!</div>
<script src="https://cdn.jsdelivr.net/npm/pixi.js@8/dist/pixi.min.js"></script>
<script src="/gamebyte.umd.js"></script>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CLAUDE.md violation: Inconsistent UMD bundle path

This demo (and 8 others) use /gamebyte.umd.js instead of the documented pattern ../gamebyte.umd.js.

CLAUDE.md rule (CLAUDE.md:324):

New demos should follow the existing pattern: Pixi CDN + ../gamebyte.umd.js, dark theme, self-contained

Affected demos using /gamebyte.umd.js:

  • 2048-demo.html
  • cookie-clicker-demo.html
  • gem-collector-demo.html
  • neon-pong-demo.html
  • quiz-game-demo.html
  • rps-game-demo.html
  • simon-says-demo.html
  • slingshot-game-demo.html
  • whack-a-mole-demo.html

Compliant demos using ../gamebyte.umd.js:

  • architecture-demo.html ✅
  • config-playground-demo.html ✅
  • hello-world-demo.html ✅

Fix: Use relative path for consistency and portability:

Suggested change
<script src="/gamebyte.umd.js"></script>
<script src="../gamebyte.umd.js"></script>

- UIAnimationSystem: Guard setNestedProperty against prototype pollution
  (__proto__, constructor, prototype keys rejected)
- GameSplash: Sanitize cfg.logoUrl in HTML template, fix incomplete
  style-tag stripping with iterative replacement loop
- PixiVersionDetection: Add input length guard to prevent regex backtracking
- archero-menu-demo: Add SRI integrity hash for GSAP CDN script
- Rebuild UMD bundle with all source fixes applied

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CodeQL fixes:
- UIAnimationSystem: Use inline key checks + hasOwnProperty for prototype
  pollution guard (CodeQL #11)
- GameSplash: Use indexOf-based loop instead of regex for style-tag
  stripping to avoid polynomial backtracking (CodeQL #13)

Demo bug fixes:
- gem-collector: Move graphics() after createGame(), fix button
  positioning with getContainer().position.set()
- whack-a-mole: Convert timer from ms to seconds (duration/1000)
- hello-world: Add stage.addChild(t) and fix i*0 → i*18 spacing

GameTopBar API alignment (6 demos):
- Replace non-existent methods (updateScore, updateTitle, updateLevel,
  updateLeftText, updateRightText, setScore, setCustomValue) with
  actual API: resources[] config + updateResource(type, value)
- Affected: 2048, cookie-clicker, gem-collector, neon-pong, simon-says,
  slingshot-game, whack-a-mole

Standards compliance:
- Fix UMD path in 9 demos: /gamebyte.umd.js → ../gamebyte.umd.js
- Move import LiveDemo to after frontmatter in overview.md
- Rebuild UMD bundle with source fixes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
canerdogan and others added 2 commits February 13, 2026 19:43
…ion, prototype guard

- GameSplash: Replace regex-based style tag stripping with pure indexOf+substring
  operations to fully resolve CodeQL polynomial regex alert
- GameSplash: Add URL scheme allowlist for logoUrl to block javascript:/data: URIs
- UIAnimationSystem: Add prototype pollution guard to getNestedProperty (matching
  setNestedProperty)
- archero-menu-demo: Fix UMD path to ../gamebyte.umd.js
- Rebuild UMD bundle

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant