Dan Freeman | | 👾

Game Time

Most of my day-to-day development is focused on building UIs, or on building tooling in support of people building UIs. It's work I enjoy and find satisfying, but occasionally I want to dig into something totally different.

In the past I've had a few different go-to projects for when that something different mood struck:

  • The assignments for Stanford's experimental "build an RPi OS in Rust" course.
  • Bob Nystrom's excellent Crafting Interpreters series, building the tree-walk interpreter in TypeScript and the bytecode VM in Rust.
  • A concept for a programming language playground, composing the syntax and semantics of a toy language on the fly out of small modular parts.

Recently, though, Nintendo announced a remake of 1993's Link's Awakening, and most likely thanks to that, I've had retro hardware on my mind.


Somewhere in an Atlanta thrift shop in the mid 90s, age ~6 Dan had a stroke of luck. In a taped-shut clear plastic bag with "50¢" Sharpied on it, nestled among a bunch of random bric-a-brac, was a gray plastic rectangle with a brightly colored sticker on it: a copy of Tetris for Game Boy.

I was so excited about the prospect of slotting together tiny pixellated blocks ("even in the car!") that I completely missed that there was a second game in the bag. Down at the bottom, with a much more unassuming design, was a copy of Link's Awakening, Nintendo's first foray into handheld gaming with its Legend of Zelda series.

I lost interest in Tetris after about a half hour of play time, but Link's Awakening captured my attention in a way that no other game up to that point in my life ever had. It was my first experience with the persistent RPG elements that would become bread and butter for most of my age group when the Pokémon craze swept through a year or two later. I spent hours guiding Link through dungeon after dungeon, each one yielding a new way to access unexplored parts of the little world I was learning my way around.

All of that is to say that this new remake has me looking back fondly on afternoons whiled away squinting at a tiny green LCD, making a little 8x8 sprite swing a sword, and somewhere in the course of that reminiscing an idea struck. That hardware had to be pretty simple, right? A power constrained, super slow processor[1]; a handful of kilobytes of RAM;[2] and just a minuscule little monochrome screen[3], right?


Enter what I'm tentatively calling gumball: a TypeScript emulator for the original Game Boy system. I've spent some time reading through some resources about the system[4], and I'm reasonably confident that a) this is an attainable spare-time project, and b) targeting a JS runtime environment isn't a completely awful idea.

I considered trying to use some language I only have a passing understanding of, like Go or Haskell or something on the JVM, but I figure I'll already be learning about enough concepts I'm not really familiar with. Complicating things further is likely to be a recipe for an abandoned project.

Targeting WASM via C or Rust or possibly AssemblyScript also crossed my mind, but this doesn't seem like the ideal project for figuring out good patterns for communicating across the JS/WASM boundary performantly. I'm imagining that's going to end up being fairly important, given that I'll eventually want to do things like push pixels to a canvas 60 times a second and synthesize audio.

So for the moment, the plan is regular script code running on the main UI thread, under the assumption that I can accomplish a lot more than one Game Boy frame's worth of work in 16ms in a modern browser on a modern device. We'll see if that turns out to have been a reasonable choice.

As of yet, no code has been written, but an empty git repository exists, and I'm excited to read more about the system specs and start figuring out how I'm going to build this thing.

Link's Awakening title screen

[1] A 4MHz clock, and all instructions take a multiple of 4 cycles, so not exactly a powerhouse. ⤴️

[2] 8KB each of working RAM and video RAM, but it turns out cartridges could (and frequently did) bring their own additional memory! ⤴️

[3] Actually, I was wrong about this one. It turns out it had 2-bit grayscale (greenscale?) support. ⤴️

[4] It turns out many, many people have had this idea before me. ⤴️