The Problem
Every retro gamer knows the struggle: you want to play Pokemon Emerald or Zelda: The Minish Cap, but you're stuck at a computer without your GBA, or you're on a Chromebook at school, or you just don't want to install sketchy emulator software.
I built EggerMath to solve this — a free, browser-based emulator that runs GBA, GBC, and GB games instantly. No downloads, no ads, no signup. Just click and play.
Here's how I built it using mGBA WebAssembly, Astro, and Cloudflare Pages.
Architecture Overview
┌─────────────────────────────────────────┐
│ Astro Static Site (SSG) │
│ 52 game pages × 11 languages = 651 │
├─────────────────────────────────────────┤
│ mGBA WASM (WebAssembly) │
│ Compiled from C → WASM with pthreads │
├─────────────────────────────────────────┤
│ MEGA SDK (client-side ROM loading) │
│ ROMs served from MEGA CDN │
├─────────────────────────────────────────┤
│ Cloudflare Pages (edge deployment) │
│ COOP/COEP headers for SharedArrayBuffer│
└─────────────────────────────────────────┘
Step 1: mGBA WebAssembly Compilation
The core challenge was getting mGBA — a mature GBA emulator written in C — to compile to WebAssembly. The mGBA project provides a WASM build that works with pthreads for multi-threaded emulation.
Step 2: COOP/COEP Headers (The Tricky Part)
WebAssembly with pthreads requires SharedArrayBuffer, which browsers only enable when the page has Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp.
But Cloudflare serves JS and WASM files with its own headers. I had to strip COOP/COEP from static asset responses while keeping them on HTML pages:
# public/_headers
/*
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
/*.js
Cross-Origin-Opener-Policy:
Cross-Origin-Embedder-Policy:
/*.wasm
Cross-Origin-Opener-Policy:
Cross-Origin-Embedder-Policy:
This lets the main page enable SharedArrayBuffer while WASM/JS files load without conflicting headers.
Step 3: ROM Loading via MEGA SDK
Instead of hosting ROMs myself (legal gray area + bandwidth costs), I integrated the MEGA JavaScript SDK to load ROMs directly from a MEGA folder. ROMs load client-side, cached in IndexedDB for repeat plays. The MEGA folder acts as a free CDN with generous bandwidth.
Step 4: Astro Static Generation
I chose Astro for its static-site generation. Each game gets its own page with SEO-optimized titles, 11 language translations with proper hreflang tags, VideoGame schema for rich snippets, and GEO-citable answer blocks for AI search engines.
This generates 651 pages — each one an indexable landing page for a specific game.
Step 5: Save States with IndexedDB
Players need save states. I implemented a 10-slot save system using IndexedDB. States persist across sessions. The Continue Playing bar shows recently played games with cover art and progress.
Step 6: Mobile-First Touch Controls
The emulator needed to work on phones. I built D-pad and A/B buttons with 3D CSS styling, touch event handlers for mobile interaction, fullscreen mode with landscape lock via Screen Orientation API, and an iOS compatibility shim for older Safari versions.
Results
- 52 games across GBA, GBC, and GB
- 11 languages (EN, PT-BR, ES, JA, DE, FR, RU, KO, IT, ID, AR)
- 651 pages built in ~10 seconds
- Zero ads, zero signup, zero download
- Save states with 10 slots per game
- Full-screen mobile with touch controls
- Deployed on Cloudflare Pages with unlimited bandwidth
What I Learned
- WebAssembly + SharedArrayBuffer is fragile — Browser security models conflict with WASM threading. The COOP/COEP header dance is non-obvious.
- Static generation scales — 651 pages generate in 10 seconds. No server needed.
- MEGA as a CDN works — Client-side MEGA SDK loads ROMs without server-side hosting. Legally safer, practically free.
- i18n is underrated for SEO — 11 languages × 52 games = 572 unique indexable pages. Most emulators only target English.
- AI search needs citable blocks — I added front-loaded answer paragraphs specifically designed to be cited by ChatGPT, Perplexity, and Google AI Overviews.
Try It
eggermath.com — pick a game, click play. Works on desktop and mobile.
The source code for the WASM integration is at github.com/rayanbaig796-crypto/gba-wasm-web.
Built with Astro, mGBA WASM, MEGA SDK, and Cloudflare Pages.
Top comments (0)