DEV Community

Gidi Dafner
Gidi Dafner

Posted on

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

I spent months rewriting Pokemon Yellow from scratch in TypeScript. Not an emulator — a complete reimplementation, ported instruction-by-instruction from Z80 assembly into HTML5 Canvas.

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

Why?

Because I wanted to know if it was possible. The pret/pokeyellow community has fully reverse-engineered Pokemon Yellow into readable assembly. I thought: what if I could take that assembly and turn it into something that runs natively in the browser?

The entire engine is ~280KB of TypeScript. Zero game assets are shipped — everything is extracted from your own ROM file directly in the browser.

How the ROM Extraction Works

This was the most interesting technical challenge. When you upload your ROM, the game:

  1. Validates it via SHA1 hash
  2. Runs 14 specialized extractors that parse the raw binary — Pokemon stats, move data, map layouts, dialogue text, trainer parties, audio sequences
  3. Decompresses Gen 1 sprites using the original compression algorithm (RLE + bit-pair encoding + delta decoding)
  4. Decodes 1bpp/2bpp tile graphics from Game Boy VRAM format into ImageData
  5. Caches everything in IndexedDB so return visits load instantly

The ROM never leaves your machine. The production build contains only the engine — no copyrighted content.

Porting Z80 Assembly to TypeScript

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

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

And turning it into this:

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

Every detail matters. SRA is an arithmetic right shift (preserves the sign bit) — use SRL (logical shift) by mistake and your music plays at wrong pitches. I made that mistake. Twice.

The Audio Engine

The Game Boy has 4 sound channels: two pulse waves, one programmable wave, and one noise channel. I built a ScriptProcessorNode-based synthesizer that generates samples at 44.1kHz, emulating all four channels.

The music engine ticks at 59.7Hz (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. The title screen music hits different when you know every byte was parsed from a 1998 ROM.

Gen 1 Battle System — Bugs and All

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

  • Focus Energy bug — it divides your crit rate by 4 instead of multiplying. Yes, the original game has this backwards. We keep it.
  • 1/256 miss glitch — moves with 100% accuracy can still miss 0.4% of the time due to an off-by-one error in the hit check
  • Badge stat boosts stacking — the Burn/Paralyze stat reduction re-applies badge boosts each time, causing stats to overflow

334 battle tests verify every edge case matches the original game.

What's Playable

This is a demo covering the first chunk of the game:

  • Title screen with animated Pikachu and full music
  • Prof. 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
  • Pokedex, party menu, items, shops, PC, save/load
  • Touch controls for mobile/tablet

The Legal Angle

The repo contains zero copyrighted assets. No sprites, no music, no dialogue, no ROM. Users provide their own legally obtained ROM, and everything is extracted client-side. The same model that emulators and projects like OpenMW use.

Tech Stack

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

Try It

Live Demo — upload your ROM and play in the browser. Works on desktop and mobile.

GitHub — MIT licensed. PRs welcome.

Built with Claude Code as a coding partner.

Top comments (0)