How it's made · Rust · October 2026

One game, three machines

A browser auto-battler built in Rust and Bevy, then demade for the Game Boy Advance and the Nintendo 64. All three run the same rules code, so the two consoles play the same match from the same seed.

BrowserBevy, 3D, up to 16 players online
Nintendo 64libdragon + tiny3d, rules via wasm2c
Game Boy Advanceagb, the same crate built no_std

The rule that made it cheap

Every rule lives in one Rust crate, sim: units, shop, economy, synergies, combat, the bots. It uses integers only (#![deny(clippy::float_arithmetic)]), and has no clock, no file system, no threads and no OS randomness. A seed and player commands go in; events come out. The clients only draw what the events say happened.

That rule was written so replays and headless balance runs would be easy. It also meant a console port is mostly a new way of drawing.

Getting Rust onto an N64

The GBA takes the crate directly: sim is no_std with std as a default feature, so the cartridge builds it for thumbv4t-none-eabi with default-features = false. The N64's MIPS CPU has no comfortable Rust target, so the rules take a detour:

lite + sim→wasm32→wasm2c→245k lines of C→libdragon gcc→.z64
  • wasm2c is set up for the console: big-endian loads and stores, no mmap, explicit bounds checks.
  • The allocator is dlmalloc over one 256 KB static arena. Memory never grows, because memory.grow cost 179 ms a page. A whole match peaks near 60 KB.
  • A test parses the C header from Rust, so the two sides of the boundary cannot drift.

The same match, proven

Both consoles advance the sim 20 times a second: the GBA one tick every three frames at 60 fps, the N64 two every three at 30 fps. An autoplayed seed 42 is therefore the same match on both. A 64-bit FNV hash of every event is pinned in the tests on all three:

lite::SEED_42_HASH = 0x1302295732caf6bb

The N64 build generates its copy of the pin from the Rust constant, the ROM prints it at boot, and an emulator test fails unless the match ends on it. No hash is copied by hand.

Seed 42GBA left, N64 right, aligned round by round

What is shared

Browser · desktop · AndroidGBAN64
Rules: units, shop, synergies, combatsimsimsim via wasm2c
Roster (74 units, 13 guilds, 10 banners)yesyesyes
Bots and their skill tiersyesyesyes
Seeded RNG streamsyesyesyes
Console runner: pacing, HUD and news as datanolitelite
Art source: the 3D models, baked card artyessprites cut from itmodels reduced from it
Match hashnopinned in testsprinted by the ROM, checked

What is not

Full gameGBAN64
RendererBevy, PBR, glTF characterstiles, 16×16 spritestiny3d, 296–549 triangle characters
Inputmouse, touch, drag and dropD-pad cursorstick cursor, four pads in party mode
Players2–16, online roomsyou + 3 botsyou + 3 bots, or 4 humans in party mode
Items and lootyesnono
LanguagesEnglish, NorwegianEnglishEnglish
SavesSQLite, local storage, server16 bytes of SRAM16 bytes of EEPROM

The consoles play a match the full game could play too: the same sim with a different MatchConfig (a roster drawn from the seed, no loot). Party mode's rules are opt-in fields in sim itself, off by default.

One art source, three budgets

  • Full game. 3D characters on one shared rig, some of them built headless in Blender. The client bakes each unit's card art.
  • GBA. Each card's art cropped to head and torso at 16×16, cut to 7 colours on its guild's palette, outlined in 1 px. Sixteen sprite palettes cap the roster at 14 guilds.
  • N64. Blender and Fast64 weld and decimate each 5,600–8,900 triangle model to about 320, skin it rigidly and pack it into one 64×64 CI4 texture.

One script regenerates everything a new unit needs on every platform, and CI checks nothing is missing.

On real hardware

The GBA cartridge plays on an Analogue Pocket through its openFPGA GBA core, and the N64 ROM plays on an Analogue 3D through a SummerCart64. Both are tested and working, as well as in mGBA and ares. Download them.

Numbers

Commits, 6 Sep – 15 Oct 2026
446
GBA port
about 2 days
N64 port, with party mode
about 3 days
GBA cartridge
970 of 1,024 KiB
N64 ROM
4.9 MB
N64 bot turn, worst
33 ms

Built with Claude Code

The game, the server, both demakes and their toolchains were built pairing with Claude Code: steering, testing and making the calls on one side, writing most of the code on the other. The architecture rule is what made that pace safe. Every port is a thin client over code that is already tested, and the hashes catch any drift.