Skip to content

ROMs and Emulation

60plus edited this page Jul 17, 2026 · 8 revisions

ROMs & Emulation

GamesDownloader ships a full retro-gaming module: a per-platform ROM library with hash-based metadata scraping, an in-browser emulator powered by EmulatorJS, and Couch Mode, a controller-first, full-screen console-style interface. This page covers all three. For how metadata is matched and where artwork comes from, see Scrapers & Metadata; for browsing and views shared with the rest of the app, see Library.

Contents

The ROM library

Platform selection, across themes

Modern Classic
Modern Classic
Neon Horizon Vapor
Neon Horizon Vapor

The ROM library is organised per platform (SNES, Genesis / Mega Drive, Game Boy, PS1, N64, and many more). Each platform is a directory under the ROM root; every file inside is catalogued as a ROM, scraped for artwork and details, and made playable in the browser when the platform has an emulator core.

Placing ROMs and scanning

Drop your ROM files into a per-platform folder under the ROM root inside your data volume:

data/games/roms/{platform}/

For example data/games/roms/snes/, data/games/roms/genesis/, data/games/roms/psx/. The {platform} folder name is a filesystem slug that GamesDownloader maps to the correct system, IGDB / ScreenScraper / LaunchBox identifiers, and emulator core.

Auto-created folders. On container start GamesDownloader creates a ROM subdirectory for every known platform (one folder per canonical platform, alias slugs de-duplicated), so after the first boot you will already have folders to drop files into. You do not have to create them by hand.

Once files are in place, open the ROM library in the web UI and run Scan ROMs. The scan walks the ROM root, detects platforms and files, and upserts everything into the database. It reports how many platforms and ROMs were found, how many are new, and how many were updated. A single-instance lock means only one scan runs at a time; you can poll its status while it works. ROMs that have been removed from disk are flagged as missing rather than silently deleted.

Structure note. Both layouts work: ROM files placed directly in data/games/roms/{platform}/, or nested one level deeper in data/games/roms/{platform}/roms/. The scanner picks up either.

Supported files and archives

The scanner recognises a broad set of ROM extensions per system (for example .nes, .sfc, .smc, .z64, .gb, .gba, .nds, .md, .bin, .cue, .chd, .iso, .pbp, and more), plus compressed archives.

Archive Catalogued Hashing behaviour
.zip + Hashes the largest file inside the archive (the actual ROM), not the archive wrapper
.7z + Same: extracts and hashes the inner ROM (requires the py7zr library, bundled in the Docker image)

Hashing the uncompressed content is what lets a zipped ROM match the same database entry as its unzipped twin, exactly how ScreenScraper and EmulationStation identify games.

Metadata scraping

Scraping runs in a single pass across multiple providers and is primarily hash-based: the scanner computes CRC32 / MD5 / SHA1 from the ROM content (from inside the archive where applicable) and matches on those hashes first, falling back to the cleaned filename and a name search when no hash match is found.

Sources combined for the ROM library:

Source Cover Hero Screenshots Support / Bezel / Wheel Video Description Score
ScreenScraper + + + + + + +
IGDB + + + +
LaunchBox + + + + + +
SteamGridDB + + +
Plugins + + + + + +
HowLongToBeat (playtime)

For the full matching logic, provider credentials, the 10-tab Edit Metadata editor, and per-source Apply buttons, see Scrapers & Metadata. Metadata plugins contribute to the ROM library exactly as they do for GOG and custom games; see Plugin Development.

Covers and aspect ratios

The library grid is platform-aware: box art is displayed at the aspect ratio that matches each system's real packaging, so covers do not stretch.

Ratio Typical platforms
3/4 (portrait, default) N64, GameCube, Wii, PlayStation, Xbox, DS, most modern systems
16/11 SNES, Super Famicom, Famicom, PC Engine, Neo Geo
4/3 (landscape) Genesis / Mega Drive (US / EU), Master System, Saturn, Dreamcast
1/1 (square) Game Boy, Game Boy Color, Game Gear, Atari 2600

The default preferred cover is the 2D box front (box-2D) from ScreenScraper, with the region left at ScreenScraper's own preference. This can be tuned per platform through the scrape presets (cover type, region, and extra artwork), and any cover can be swapped manually in the Edit Metadata panel.

In-browser emulation

A ROM detail page, across themes

Modern Classic
Modern Classic
Neon Horizon Vapor
Neon Horizon Vapor

Playable ROMs run directly in the browser through EmulatorJS, with no client-side installation. EmulatorJS drives RetroArch cores compiled to WebAssembly, so a wide range of systems is supported from one interface: NES, SNES, GBA, GBC, Game Boy, Genesis / Mega Drive, N64, PS1, arcade, and many more. Platforms without a matching core are still catalogued in the library but are not launchable.

Press Play on a ROM's detail page to open the display-mode picker, then start the game.

Display modes

Three display modes are offered when you launch a game, and your choice is remembered for next time:

Mode Behaviour
Fullscreen Covers the entire viewport as a top-level page
Window A floating, resizable panel embedded in the current page
New Tab Opens the emulator in a dedicated browser tab

Multithreading. Fullscreen and New Tab load the player at the top level and can use multithreaded cores when threads are enabled in settings (this relies on COOP / COEP headers, which the server sets). The floating Window panel runs in an iframe and is single-threaded, which keeps it lightweight for quick sessions.

Save states and battery saves

The emulator supports two kinds of persistence, both stored server-side, per user, per ROM:

  • Save states capture the exact machine state at any moment and can be restored later. Each save state stores a thumbnail screenshot taken at the point of saving.
  • Battery saves (SRAM) are the game's own in-cartridge saves; they are uploaded automatically when the game exits, so your progress survives across sessions and devices.

A built-in load-state picker lets you browse both lists without leaving the player. It has separate tabs for save states and battery saves; with a controller you load the highlighted entry with A, delete an individual entry with X, switch tabs with the shoulder / d-pad, and close with B.

Uploaded save files are virus-scanned (ClamAV) and counted against your storage quota before being written.

The Game Saves panel

You manage all of your save data from the Game Saves panel on your Dashboard. It lists every game you hold saves for, one folded line each, and lets you play from a save, upload, download, delete, and back everything up without leaving the page.

Game Saves panel

One line per game. Every game shows as a single row with its cover, name, platform, and total size. On the right is a rail of nine numbered slot chips plus a battery marker: a tinted chip is a used slot, a dashed one is free, so you can see which slots each game has filled without opening anything. Click a row (or press Enter) to unfold that game's full memory-card grid underneath; only one game opens at a time, which keeps the panel compact no matter how many games have saves. When more than one game has saves, you can sort the list by Recent or Largest.

Slot tiles. Inside an unfolded game, each of the nine save-state slots is a tile. A filled slot shows the screenshot captured at the moment you saved, a Slot N badge, and the save's date and size - click the screenshot to enlarge it. An empty slot shows a dashed + tile you can upload into. Save states written before slots existed appear as separate Older tiles rather than being forced into a slot.

Battery tile. The battery (SRAM) save appears as its own tile showing the game's cartridge or disc art, since a battery save is the cartridge's memory. There is only ever one battery save per game - it holds the whole chip, including the game's own in-cartridge save slots - and it is refreshed automatically as you play. If a game has no battery save yet, the tile shows the cartridge greyed out.

Play from a save. Press play on any slot tile, an Older tile, or the battery tile to launch the game resuming from exactly that save. The ROM's detail page still asks how you want the window (fullscreen, floating window, or new tab) - only which save to load is chosen for you.

Uploading a save from your machine. Click the + on an empty slot to upload a .state file straight into that slot, or the empty battery tile to upload a .srm. Uploaded files are virus-scanned and counted against your quota before they are written, and a slot upload replaces whatever was in that slot.

Downloading a save. The download button on any tile saves that single save to your machine as a small zip. The archive also records which game and slot the save belongs to, so it can be dropped back onto any GamesDownloader server later.

Export everything and restore. Use Export all at the top of the panel to download a single gd-saves-<date>.zip containing every state and battery save you own - your full backup. The download button on a game's own row exports just that game's saves. To restore, use Import: a GamesDownloader archive names each save's game and slot in its manifest, so it routes itself, matching games on this server by content hash and name. If a game in the archive is not on this server the remaining entries still import and you are told which one was skipped. You can also import a bare .state or .srm, but because a loose file carries no such information you upload it through the + on a specific slot or the battery tile so it knows where it belongs.

Storage quota. A quota bar across the top of the panel tracks your total save usage against your allowance and turns amber as you approach the limit. Uploads that would exceed the quota are rejected with a clear message, and deleting old saves frees the space immediately. The default allowance and per-account overrides are described in Game Saves tab and quota below. As with the rest of the app, you only ever see your own saves.

Bezels and gamepads

  • Bezel overlay. When a game has bezel artwork (a PNG with a transparent centre), you can toggle a decorative frame over the game canvas. The toggle is per game and remembered across sessions. Bezels are only offered for ROMs that actually have bezel art.
  • Gamepad support. Controllers are read through the browser Gamepad API. Chromium-based browsers are recommended; Firefox has limited gamepad support in embedded contexts.

Game Saves tab and quota

Each user manages their own save data from the Game saves section of their Dashboard - the Game Saves panel above. A quota bar there shows total disk usage against your allowance.

The default per-user save quota is 100 MB. An administrator sets the global default under Settings > Downloads (the Limits section) and can give an individual account a different quota under Settings > Users (the limits button on that user's row). Uploads that would exceed the quota are rejected with a clear message, and deleting old saves frees the space immediately. Save management follows the same Users & Permissions model as the rest of the app: each user only ever sees their own saves.

Couch Mode

Couch Mode is a full-screen, controller-first interface for browsing platforms, launching games, and managing saves entirely without a keyboard or mouse. It is reachable from the Classic theme via the user menu, and exiting returns you to /library on the Classic layout or / on the Modern layout.

Platform carousel. Systems are shown as an animated card carousel with Ken Burns photo backgrounds, platform name logos, and locale-aware scrolling descriptions (drawn from XML platform metadata in your UI language, with a fallback).

Three themes. Choose Noir, Aura, or Slick. Each restyles the cards, backgrounds, and overlays (for example Noir is a warm, cinematic, grainy look; Aura is vivid and bright; Slick is a near-dark, hard-edged treatment).

Game views. Games can be shown as a list or a grid, with configurable cover sizes (XS / S / M / L). Game trailers can play as a muted background video with a volume control.

In-game pause menu. Pressing Start + Select (or Esc) opens a custom overlay with:

Item Action
Resume Return to the game
Save State Write a new save state (with screenshot)
Load State Open the load-state picker
Emulator Settings Open the full settings system
Exit Game Leave the emulator

Full emulator settings. A 10-category settings system is driven by the live EmulatorJS API, covering Audio, Graphics (shaders, FPS, VSync, rotation), Screen Capture, Speed (fast-forward, slow-motion, rewind), Input (keyboard, mouse lock, autofire, virtual gamepad), Save States (slot, location, auto-save interval), Control Settings (per-player gamepad assignment and button remapping with live capture), Cheats, Core Options read from the core at runtime, and Restart Game. Settings persist across sessions.

Couch Menu. Pressing Start (or M) opens the couch menu for theme, view mode, launch mode, bezel toggle, cover size, and video volume.

Exit and input safety. Exiting Couch Mode goes through a controller-navigable confirmation dialog, and a short (300 ms) cooldown after any overlay closes prevents a held button from bleeding through into the view underneath.

Storage layout

ROM files and their generated media live under the data volume:

Path Contents
data/games/roms/{platform} ROM files, per platform
data/resources/roms/{platform}/{rom_id} Per-ROM media (cover, hero, bezel, wheel, video, support art)
data/saves/{platform}/{rom_id}/states/{user_id} Save states, per user per ROM. Private - never served as static files.
data/saves/{platform}/{rom_id}/saves/{user_id} Battery saves (SRAM), per user per ROM. Private - never served as static files.
data/resources/platforms Platform icons, name logos, and fanart used by the carousel

All scraped artwork is downloaded and served locally, never hot-linked from an upstream CDN.

Next: Scrapers & Metadata · Library

Clone this wiki locally