-
Notifications
You must be signed in to change notification settings - Fork 0
ROMs and 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.
- The ROM library
- Placing ROMs and scanning
- Supported files and archives
- Metadata scraping
- Covers and aspect ratios
- In-browser emulation
- Display modes
- Save states and battery saves
- The Game Saves panel
- Bezels and gamepads
- Game Saves tab and quota
- Couch Mode
- Storage layout
Platform selection, across themes
| Modern | Classic |
![]() |
![]() |
| 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.
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 indata/games/roms/{platform}/roms/. The scanner picks up either.
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.
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.
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.
A ROM detail page, across themes
| Modern | Classic |
![]() |
![]() |
| 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.
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.
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.
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.

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.
- 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.
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 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.
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
Getting Started
Configuration
Features
- Dashboard
- Library
- Collections
- Game Requests
- GOG Integration
- ROMs & Emulation
- Downloads & Torrents
- Users & Permissions
- Themes
Plugin Development
Reference







