THE PARTS BIN

The technology stack

.NET 10runtime ASP.NET Core MVCweb application Razorserver-rendered views JavaScriptplayer bootstrap EmulatorJSbrowser emulator WebAssemblynear-native execution libretro / MAMEarcade cores MariaDBpersistent data Cloudflareedge & security Apachereverse proxy MaxMind GeoIPlocal geolocation PowerShell + xUnittest & deploy

THE BIG PICTURE

How the pieces connect

Request path Server services Browser runtime

PRESS START

What happens when you launch a game?

From click to first frame, the launch descends through six guarded stages. Each hand-off moves the game closer to running locally in the browser.

  1. 01
    Choose a cabinet

    The browser requests /play/{id}. The MVC controller asks the live catalog for that game.

  2. 02
    Check the storeroom

    GameCatalog reads games.json and checks whether the matching game asset is available through the configured storage backend.

  3. 03
    Issue a short-lived key

    Opening a valid game grants this browser session access to that title for 15 minutes. It is an access gate, not DRM.

  4. 04
    Build the control board

    Razor embeds a configuration containing the core, a session-gated game URL, controls, theme colour, and optional audio samples.

  5. 05
    Load the machine

    play.js configures touch controls and keyboard focus, then downloads the EmulatorJS loader and WebAssembly core.

  6. 06
    Run it in the browser

    The browser downloads the game data and runs a WebAssembly emulation core locally. The core models the arcade hardware and executes the game program; the game ROM itself is not a WebAssembly program. Ordinary input does not need a server-rendered frame in response.

AN ARCADE MACHINE INSIDE A TAB

The browser does the heavy lifting

The server delivers the pieces, but gameplay happens on the player’s device. EmulatorJS connects a JavaScript interface to libretro cores compiled into WebAssembly. Input delay and compatibility still depend on the core, browser, device, display, and controls. A successful launch does not prove complete fidelity to original hardware.

15 secpresence heartbeat 45 secactive-player window 15 minROM access grant 20 minsession idle limit

THREE KINDS OF MEMORY

What the site remembers—and for how long

Lifetime visitors

A long-lived, HTTP-only browser cookie prevents repeat counting. First visits atomically increment a persistent MariaDB counter.

Browser cookieMariaDB

Players online

The game page sends a lightweight heartbeat every 15 seconds. A thread-safe in-memory tracker drops silent players after 45 seconds.

Heartbeat APIMemory

Security audit

New visitors, HTTP errors, scanner blocks, and unhandled failures are recorded with request context and locally resolved location data.

Audit recorderMariaDB

Player history

Signed-in game sessions persist bounded heartbeat time per game, giving each account an accurate recent-history and total-play-time view.

Session heartbeatMariaDB
DEFENCE IN DEPTH

The bouncer knows the usual tricks

Public sites attract more than players. Retro-Arcade treats security as a layered request pipeline, so suspicious traffic is stopped early and ordinary gameplay stays simple.

01Cloudflare edgeTLS termination, caching for static assets, and a shared WAF block list.
02Trusted proxy boundaryForwarded headers are accepted only from the loopback Apache proxy.
03Probe detectionTraversal, secret-file, PHP, archive, credential, and unknown-path reconnaissance is rejected.
04Automatic escalationNew public scanner addresses enter an asynchronous queue for the Cloudflare block-list API.
05Private game storageSession checks gate local and R2 delivery. Technical access controls do not establish copyright permission.
06Audit & observabilityDaily files, structured request logs, MariaDB audit events, and a colourful startup diagnostic banner.

FROM SOURCE TO SCORE

One command, guarded all the way to production

The deployment checks tests before replacing the application. Game assets are managed separately from source control and can be synchronised into private storage. A health check confirms service availability; it is not a gameplay or licensing audit.

OLD GAMES. MODERN DELIVERY.

Built like a small system, experienced like a simple cabinet.

The architecture is deliberately practical: render quickly on the server, run the demanding work in the browser, keep the catalog easy to update, place sensitive files outside the public web root, and automate the checks that make every deployment safer.

Insert another coin