I Rewrote Pokemon Yellow Entirely in TypeScript — Here's How It Runs in Your Browser

Published: (March 17, 2026 at 10:30 AM EDT)
3 min read
Source: Dev.to

Source: Dev.to

I spent months rewriting Pokémon Yellow from scratch in TypeScript. This isn’t an emulator – it’s a complete re‑implementation, ported instruction‑by‑instruction from Z80 assembly into HTML5 Canvas.

Play it here (bring your own ROM) | Source code

Overview

The entire engine is ~280 KB of TypeScript. No game assets are shipped; everything is extracted from your own ROM file directly in the browser. The ROM never leaves your machine – the production build contains only the engine, with no copyrighted content.

Engine Architecture

ROM Validation & Extraction

  • Validates the uploaded ROM via SHA‑1 hash.
  • Runs 14 specialized extractors that parse raw binary data:
    • Pokémon stats, move data, map layouts, dialogue text, trainer parties, audio sequences.

Graphics Decompression

  • Decompresses Gen 1 sprites using the original compression algorithm (RLE + bit‑pair encoding + delta decoding).
  • Decodes 1bpp/2bpp tile graphics from Game Boy VRAM format into ImageData.

Caching

  • Caches all extracted data in IndexedDB so return visits load instantly.

Assembly to TypeScript Translation

The original game runs on a Sharp LR35902 (Game Boy CPU). Porting it means translating assembly like this:

Audio1_CalculateFrequency:
    ld a, [wChannelOctave]
    inc a
    cp 7
    jr z, .done
    sra d
    rr e
    jr .loop

into TypeScript:

function calculateFrequency(octave: number, freq: number): number {
  let d = (freq >> 8) & 0xFF;
  let e = freq & 0xFF;
  for (let a = octave + 1; a > 1) | (d & 0x80)) & 0xFF; // SRA (arithmetic shift)
    const carry = d & 1;
    e = ((carry > 1)) & 0xFF; // RR (rotate right through carry)
    d = newD;
  }
  return (d << 8) | e;
}

Every detail matters – using a logical shift (SRL) instead of an arithmetic shift (SRA) changes the pitch of the music. I made that mistake twice.

Audio Synthesis

  • The Game Boy has four sound channels (two pulse, one programmable wave, one noise).
  • Implemented a ScriptProcessorNode‑based synthesizer that generates samples at 44.1 kHz, emulating all four channels.
  • The music engine ticks at 59.7 Hz (matching the Game Boy’s VBlank rate), interpreting command sequences extracted from the ROM (tempo changes, note events, duty‑cycle switches, vibrato, pitch slides).
  • 50+ music tracks and 37 sound effects are fully playable.

Battle System Accuracy

The battle system faithfully reproduces all of Gen 1’s notorious quirks:

  • Focus Energy bug – crit rate is divided by 4 instead of multiplied.
  • 1/256 miss glitch – moves with 100 % accuracy can still miss ~0.4 % of the time.
  • Badge stat‑boost stacking – Burn/Paralyze reductions re‑apply badge boosts each time, causing overflow.

A suite of 334 battle tests verifies that every edge case matches the original game.

Demo Features

  • Title screen with animated Pikachu and full music.
  • Professor Oak intro sequence.
  • Pallet Town → Route 1 → Viridian City (12 maps).
  • Wild and trainer battles with complete Gen 1 mechanics.
  • Pikachu follower with happiness system.
  • Pokédex, party menu, items, shops, PC, save/load.
  • Touch controls for mobile/tablet.

The live demo lets you upload your legally obtained ROM and play in the browser on both desktop and mobile.

Technical Details

  • TypeScript + HTML5 Canvas – no frameworks, no dependencies.
  • Vite for builds.
  • Vitest for testing (375 tests).
  • ~35 000 lines of TypeScript source.

Repository & License

  • GitHub – MIT licensed. PRs welcome.
  • Built with Claude Code as a coding partner.
0 views
Back to Blog

Related posts

Read more »