One scale manager, 70 watches
Every Garmin model has a different screen, so a single scale manager was built to size every sprite, font and hitbox off the device resolution — a 4,500-line refactor that made multi-resolution support possible at all.
From the workshop
Tiny screen. Big fun.
A growing catalogue of pixel-perfect arcade games for Garmin watches.
Retro endless runner. Jump, dodge, sprint through pixel-perfect obstacles.
Featured by Garmin Connect IQ
32,039 players · 160 countries · 432,636 runs
Space trading across the solar system. Upgrade your ship, trade goods, dock at 25+ stations.
9,685 pilots · 18,861 quick flights · 40M credits earned
A virtual pet on your wrist. Hatch eggs, raise creatures, discover dozens of species.
3,103 keepers · 7,268 skylings hatched · 78,164 play sessions
The build story
A Garmin watch is a spectacularly bad games console. The frame budget is 67 milliseconds — 15 FPS on a good day — the memory ceiling is measured in double-digit kilobytes, and the screen is a circle. SkyGames exists because that sounded like a fun set of rules rather than a reason not to try. It’s now a catalogue: SkyRunner (endless runner, 50 handcrafted levels), SkyLander (space trading), and SkyLing (a virtual pet that lives on your wrist), all sharing one Laravel backend and one set of hard-won lessons.
SkyRunner’s game loop runs player movement, two spawning systems, six kinds of collision detection, and a full render pass inside that 67ms window — with a profiler wrapped around each stage so regressions show up as numbers, not vibes. The game targets seventy watch models across eight screen resolutions, from a 218-pixel Forerunner to a 454-pixel AMOLED Fenix: everything is authored once at the largest size and every pixel literal in the codebase passes through a scale manager at runtime. Levels are authored as plain text files and compiled by a Python step into Monkey C data arrays before every build — the watch never parses a level, it just reads structs that were code all along.
SkyRunner hit the Connect IQ store in November 2025. The online level editor — build a level in the browser, play it on your wrist — followed eleven days later. By January the Pro paywall was gone and everything went free; by April there was an in-game diamond shop. Somewhere in there, Garmin featured it on Connect IQ, and the player counts on this page stopped being hypothetical — they’re live numbers from the same events pipeline the games report into, spanning real players in 160-odd countries.
SkyLing pushed the catalogue somewhere stranger: a pet that keeps living while the watch sleeps. Its decay logic extrapolates hunger and mood server-side (capped at 24 hours, and politely paused during your sleep window), and its companion watch face syncs through a pairing scheme designed for a device that can’t hold secrets: a one-time four-digit code, aggressively rate-limited so the tiny keyspace can’t be scanned, exchanged on first use for a long-lived 96-bit key. The analytics behind all three games had its own growth moment — the day the events table got big enough to OOM the dashboard, aggregation moved from PHP into SQL, and a notification now pings my phone at every fiftieth new device.
None of these shipped in a straight line. A mascot got fired, a ship grew five new bodies, a virtual pet refused to get hungry, and a font had to be drawn one pixel at a time for the cheaper watches. Below is the honest version of each game’s development — a few of the real turns, redesigns and platform fights pulled straight from the commit history. Each track scrolls sideways; drag or swipe through to dig in.
Every Garmin model has a different screen, so a single scale manager was built to size every sprite, font and hitbox off the device resolution — a 4,500-line refactor that made multi-resolution support possible at all.
The Garmin starter project shipped with a sample “monkey” asset that never actually rendered. The first real player was this scrappy little hand-drawn runner, dropped in just to start testing.
Sheep, kiwi, kākā and a bee joined the obstacle roster — each needing a hand-tuned hitbox so collisions matched the visible pixels, not the bounding box.
A Python step compiles authored levels into Monkey C data the watch just reads — plus auto-generated preview images and a stack of validation rules for ledge spacing, reachable jumps and safe landings.
→
v1.4.0 redrew the dude — and every other sprite — into a proper six-frame run cycle with new celebrate, death, jump and slide states. Same scrappy runner, glow-up applied.
For a while the free version literally hit a wall — a brick paywall that spawned across the track at 9.8km and stopped Lite players cold. v1.5.0 tore it down: every Pro licence check ripped out, all levels and the browser editor unlocked for everyone.
The diamonds players had been hoarding finally became a currency — a shop selling Endless-Mode power-ups like the slingshot, bubble and extra life. (A wallet bug shipped with it, hot-fixed two weeks later.)
The opening art drop: three workhorse ships — the nimble Mosquito, the cargo Mule and the Vector — drawn through a custom AI asset pipeline and packed across seven watch resolutions.
Flight feel jumped when the ship started rotating to face its velocity vector, with the thrust flames re-aligned to match — flat top-down drifting suddenly read as real flying.
The economy moved from flat zone multipliers to fuel-cost-per-AU, progressive distance burn, cargo-weight penalties and a mandatory landing reserve — every trade route became a fuel-vs-profit sum.
→
The Mosquito grew a five-tier upgrade ladder — Reinforced, Racer, Combat, Elite, Legendary — each a distinct sprite, the plain grey fighter becoming a gold-and-purple cruiser.
To test buying every skin, starting credits got bumped from 1,000 to 100,000 — then sheepishly walked back to 1,000 the same day. Leftover debug money, narrowly un-shipped.
Cheaper watches like the Venu 3 have no scalable fonts, so a font manager rendering 5×7 and 6×9 pixel glyphs was written from scratch — later given borders and shadows to stay legible.
The new toast notifications spawned a timer each and hit Garmin’s hard timer limit; the fix routed every auto-dismiss through one shared timer. A very watch-platform kind of war story.
SkyLing didn’t start as a roster — it started as Drago, a single red dragon, the art-pipeline guinea pig. A cast of named characters followed (including Zorro, the family border collie) before the whole lot was cut and rethought.
Those named characters — and a three-egg picker — got scrapped for a single Mystery Egg. Every Skyling now starts identical, and which of ~27 species you get is decided at hatch time by how active you’ve been.
An eight-hour offline test exposed that hunger never decayed while the app was closed. The fix pinned the real-time-to-game-time ratio and hardened the decay maths against nulls — the first real offline-simulation milestone.
A single batch generated configs and sprites for thirteen new Skylings at once, growing the roster toward its eventual ~27 species across bio, cosmic, synthetic and oddball families.
→
A wholesale art pass regenerated every frame of the Buzz creature — idle, eat, emerge — a clean before/after on the very same sprite.
Older watches were rendering text wrong, so a font manager and bitmap fonts were added with per-context scaling — menu, important, detail — to keep type sharp across the device zoo.
The companion watch face arrived: the app makes a short pairing code, pushes signed state to the backend, and the face polls to mirror your live pet. The code shrank from six letters to a four-digit PIN to make it typeable on Garmin Connect.
Friendly Mode (reduced decay, no death) and Pause-During-Sleep landed — your Skyling’s hunger and mood freeze overnight, with a 24-hour cap so a long-unsynced pet can never come back already starved.
SkyGames isn’t only the things that shipped. Two bigger swings live in the SkyLands universe — a dark-fantasy world of islands shattered across a breathable void, a setting I built out far past any single game — and neither has quite earned its place on a watch yet. Parked, not dead.
Real-time action RPG · Diablo-inspired
Procedurally-generated floating islands, auto-combat, a full town with shops and quests, bosses, an economy — genuinely most of a game. It also kept losing a fight with the hardware: on a real watch the frame rate sat near six per second where the simulator flew, the watchdog timer kept killing map generation until it was sliced into async chunks, and a core render cache quietly refused to cache. The closest thing here to finished — and the clearest case of asking a wrist computer for a little too much.
Endless text RPG · Darklands-inspired
A data-driven, endless RPG in the spirit of Darklands — branching vignettes, reputation, turn-based combat, all authored as data and compiled onto the watch. The engine works. What ran away was the world: a 500,000-word lore bible across 200-odd wiki pages, declared ‘complete’ three days in and promising six hundred quests — against a game with exactly one quest and one location actually playable. It didn’t get out of hand so much as start out of hand. Shelved, with enormous fondness.
Both wear the same world on their sleeve, and it’s a world I fully intend to come back to.
Because constraints are a forcing function. There’s no room for a framework, an asset store, or an unconsidered allocation — every feature has to justify its bytes. The catalogue keeps growing (there are more Sky-somethings in various stages of playable), and every one of them starts from the same question that started SkyRunner: how much fun fits in 67 milliseconds?
Working with a brutal platform constraint that still has to feel effortless? Here’s how we could work together.