Speedy Bird Manual

Builder's Manual · Night Arcade Cabinet

Assets and Sprites

Source-backed guides to the speed curve, ReactLynx architecture, sprites, native hosts, documentation site, and release pipeline.

9Manual pages
+1%Speed / pipe
iOSSource scaffold

Assets and Sprites

Game sprites are pre-sliced individual PNG files. The ReactLynx renderer uses a separate <image> element for each visual rather than slicing a sprite sheet at runtime.

Directory Structure

assets/
├── sprites/
│   ├── bird-0.png              # Wings down
│   ├── bird-1.png              # Wings level
│   ├── bird-2.png              # Wings up
│   ├── background.png          # Sky + city background tile (276x228)
│   ├── ground.png              # Ground tile (224x112 source, drawn at 224x129)
│   ├── get-ready.png           # "Get Ready" overlay (174x160)
│   ├── game-over.png           # Game over panel with score boxes (226x158)
│   ├── pipes/
│   │   ├── pipe-top.png        # Top pipe body tile
│   │   ├── pipe-top-mouth.png  # Top pipe mouth (lip)
│   │   ├── pipe-bottom.png     # Bottom pipe body tile
│   │   └── pipe-bottom-mouth.png # Bottom pipe mouth (lip)
│   ├── digits/
│   │   └── digit-0.png … digit-9.png  # Score display (18x27 each)
│   └── medals/
│       ├── medal-bronze.png
│       ├── medal-silver.png
│       ├── medal-gold.png
│       └── medal-platinum.png
│
└── audio/
    ├── sfx_wing.wav            # Flap
    ├── sfx_point.wav           # Score
    ├── sfx_hit.wav             # Pipe collision
    ├── sfx_die.wav             # Ground collision
    └── sfx_swooshing.wav       # Game reset

How Assets Are Loaded

In the Lynx app, assets are loaded via top-level import statements at the module level:

import bird0 from '../../assets/sprites/bird-0.png';
import bird1 from '../../assets/sprites/bird-1.png';
import bird2 from '../../assets/sprites/bird-2.png';

const BIRD_SPRITES = [bird0, bird1, bird2, bird1]; // ping-pong cycle

lynx.config.ts sets an image inline limit of 64 KiB, large enough for every current game sprite. Rspeedy embeds those images as data URLs so native hosts can load the single bundle offline. Audio imports are emitted as separate WAV assets; native audio remains a placeholder rather than a packaged playback implementation.

Pipe Rendering

Pipes use a tile-based approach instead of stretching a single image. Each pipe is composed of:

  1. Body tiles — repeated vertically to fill the pipe height (extends well past the screen edge)
  2. Mouth tile — placed at the opening where the bird flies through

This avoids visual stretching artifacts and matches the original game’s pixel-art style. Each tile is 55px wide and ~53px tall.

Background and Ground Tiling

Both the background and ground use 5 copies laid out horizontally in a flex row. The container is translated left via CSS transform. Background offset wraps at one tile width (276px); ground offset wraps at half its tile width (112px), matching the repeated ground pattern.

  • Background: 5 tiles at 276px each = 1380px total, scrolls at 0.2 * speedMultiplier px/frame
  • Ground: 5 tiles at 224px each = 1120px total, scrolls at 2.7 * speedMultiplier px/frame

Score Display

The in-game score uses sprite-based digit rendering (ScoreDisplay.tsx). Each digit is drawn in an 18x27px <image> element with 2px gaps, centered on screen. These are display dimensions, not the source PNG dimensions (for example, digit-0.png is 12x18px).

The game-over panel score uses <text> elements positioned absolutely over the panel sprite.

Credits

Website copies and social assets

The Canvas demo serves copies from docs/public/assets/. Keep each sprite and sound byte-identical to its canonical counterpart in root assets/ when updating assets. The docs build copies public files unchanged; it does not synchronize those two trees automatically.

Social previews and icon fallbacks are generated by docs/scripts/generate_social_assets.py. It uses the Pillow version pinned in docs/scripts/requirements.txt, accepts display/monospace font paths, and generates a 1200x630 og-image.png. See docs/README.md for commands and platform-specific font examples.