Skip to content

quiet-build/flappy-3d

Repository files navigation

Flappy 3D

A polished 3D Flappy Bird-style PWA built with Three.js (vanilla, no React/R3F). Cel-shaded graphics, full audio, offline-capable via service worker.

Live: quietbuildlab.github.io/flappy-3d/

v1.1 — Beauty Pass

  • Title screen comes alive: bird hover-bobs, demo pipes scroll past, logo letter-stagger entrance, "Tap to start" pulses
  • In-game juice: +1 score popups, milestone celebrations at 10/25/50 (gold burst + flash), optional flap trail (Settings), 4-color pipe cycling
  • Glass UI: Press Start 2P arcade font on headings, backdrop-filter blur on overlays, gradient buttons with hover/active depth, 2-layer focus ring
  • Every motion effect respects prefers-reduced-motion (OS) and the in-game motion toggle
  • Bundle: 196KB / 250KB gzipped

v1.0 — Core Game

  • Endless Flappy Bird loop: tap/click/spacebar to flap, gravity falls, pipes scroll, score on pass, die on contact
  • Cel-shaded toon materials, post-processing (bloom + vignette) gated on desktop / hi-tier mobile
  • 5 screens (Title, HUD, Pause, GameOver, Settings) with leaderboard + persistence
  • PWA installable, offline play, Lighthouse PWA 1.00, iOS Safari audio unlock
  • Accessibility: keyboard nav (Space/Enter/Esc/Tab), focus rings, ARIA-live score, colorblind palette toggle, motion-reduce override
  • xstate v5 state machine, GSAP juice (squash, screen shake, particles), Howler audio with WebAudio synth fallback

Bundle Budget

Production JS bundle must stay ≤ 250 KB gzipped (PERF-01).

Current baseline (Phase 3): 194.49 KB gzip — 55.5 KB headroom.

Check the bundle size

npm run bundle-check

This runs npm run build and then scripts/bundle-check.sh, which:

  • Gzips all dist/assets/*.js files
  • Compares total gzip bytes against the 256,000-byte (250 KB) limit
  • Exits non-zero if the limit is exceeded

Inspect bundle composition

After any build, open dist/stats.html in a browser to see the interactive treemap (generated by rollup-plugin-visualizer). Large chunks to watch:

  • three — should be tree-shaken; verify no wildcard imports (import * as THREE) in src/
  • gsap — ~28 KB gzip (core only)
  • howler — ~15 KB gzip
  • preact — ~4.7 KB gzip

Performance Testing

PERF-03: Sustained 60fps on iPhone 12 / Pixel 6 class device during normal play.

This is a manual test — no automated gate exists for frame rate on real hardware.

Procedure (run before each release)

  1. Build and serve:

    npm run build && npx serve -s dist -l 5000

    Or deploy to GitHub Pages and test the live URL.

  2. On a mid-tier mobile device (iPhone 12, Pixel 6, or similar — NOT an emulator):

    • Open in Chrome for Android or Safari on iOS
    • Open DevTools (Android: chrome://inspect, iOS: Safari > Develop menu)
    • Navigate to Performance panel → enable FPS Meter overlay
  3. Test scenario:

    • Start a game run
    • Let 20–30 pipes pass (score ≥ 20 so difficulty has ramped)
    • Confirm FPS stays ≥ 58 fps throughout; brief dips to 55 on pipe spawn are acceptable
  4. If FPS drops below 55:

    • Check navigator.hardwareConcurrency in DevTools console — should be ≤ 4 on mid-tier, which gates bloom off
    • Check that createComposer returns null on the test device (no EffectComposer overhead)
    • Reduce POOL_SIZE or review particle count if allocations are occurring during gameplay
  5. Record result in .planning/STATE.md Performance Metrics table.


Hardening verification

Run these checks before tagging a release. Each maps to a Phase 5 Success Criterion.

SC-1: Memory stability (PERF-05)

  1. Open https://quietbuildlab.github.io/flappy-3d/ in Chrome desktop (DEV build: npm run dev)
  2. Open DevTools → Console
  3. Play 10 death + restart cycles
  4. Observe [mem probe] round=N geometries=X textures=Y log lines (DEV build only)
  5. Pass: geometries and textures values plateau — no consistent growth across rounds

Alternate (any browser): DevTools → Memory → take Heap Snapshot before and after 10 rounds. WebGLBuffer count should not grow.

SC-2: iOS audio — real device (AUD-01, AUD-02)

  1. Open https://quietbuildlab.github.io/flappy-3d/ in Safari on a real iOS device (iOS 16+)
  2. Tap the screen to start a game
  3. Confirm flap, score, and death sounds play immediately (not synth oscillators)
  4. In Safari Web Inspector (Mac → Develop menu → your device), run: Howler.ctx.state — expected: "running"
  5. Ringer ON: all audio plays normally
  6. Ringer OFF (silent switch): audio is silenced by iOS — this is expected behaviour, not a bug. The Settings modal documents this.
  7. Pass: Howler.ctx.state === 'running' after first tap; SFX are recognisably flap/score/death sounds

SC-3: Tab-blur pause/resume

  1. Open the game and start a round
  2. Switch to a different tab (or press Cmd+H on mobile to background the browser)
  3. Expected: music stops immediately; game transitions to paused state
  4. Return to the tab
  5. Expected: Pause screen is shown; score is preserved
  6. Tap RESUME
  7. Expected: music resumes; game continues
  8. Pass: All three steps confirmed correct. No music ghost-playing after tab-switch.

SC-4: No stopped-actor warnings, no listener accumulation

  1. Open https://quietbuildlab.github.io/flappy-3d/ in Chrome
  2. Open DevTools Console (filter: Warnings + Errors)
  3. Play 20 death + restart cycles
  4. Pass: Zero "Event sent to stopped actor" warnings

Listener stability check (Chrome only):

  1. After page load (before any play): Object.values(getEventListeners(window)).flat().length — record baseline
  2. After 10 restart cycles: run same command
  3. Pass: Count is identical (or within ±1 for browser internals)

Deployment

The game is deployed to GitHub Pages at: https://<owner>.github.io/flappy-3d/

One-time setup (per repository)

  1. Go to Repository Settings > Pages
  2. Set Source to GitHub Actions (not "Deploy from branch")
  3. Save. No branch selection needed — the workflow handles everything.

Automatic deploy

Push to main triggers .github/workflows/deploy.yml which:

  1. Installs dependencies (npm ci)
  2. Builds the production bundle (npm run build)
  3. Bundle size gate: fails if JS gzip > 250 KB (scripts/bundle-check.sh)
  4. Uploads dist/ as a GitHub Pages artifact
  5. Deploys to https://<owner>.github.io/flappy-3d/
  6. Lighthouse PWA gate: runs Lighthouse in headless Chrome against the live URL and fails if PWA score < 0.90 (PERF-05)

Manual deploy (local preview)

npm run build
npx serve -s dist -l 5000
# Open http://localhost:5000/flappy-3d/

CI artifacts

After each workflow run, two artifacts are available for download:

  • bundle-statsdist/stats.html interactive treemap (rollup-plugin-visualizer)
  • lighthouse-reportlighthouse-pwa.json full Lighthouse audit

Cloudflare Pages migration (future)

If migrating from GitHub Pages to Cloudflare Pages:

  1. Change vite.config.ts: base: '/flappy-3d/'base: '/'
  2. Re-audit dist/manifest.webmanifest for start_url/scope — they will change to /
  3. Delete .github/workflows/deploy.yml
  4. Connect repo to Cloudflare Pages dashboard (auto-builds on push, same npm run build command, output: dist/)
  5. No application code changes required.

Releases

No releases published

Packages

 
 
 

Contributors