I Rewrote Pokemon Yellow Entirely in TypeScript — Here's How It Runs in Your Browser
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 .loopinto 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.