Skip to main content
GameLord’s configuration is split between environment variables (for API credentials and build settings) and runtime configuration (stored in the user data directory).

Environment variables

Create a .env file in apps/desktop/ to configure optional features:
# ScreenScraper API developer credentials
# Register at https://www.screenscraper.fr/forumsujets.php?frub=12&numpage=0
SCREENSCRAPER_DEV_ID=
SCREENSCRAPER_DEV_PASSWORD=
Never commit .env to version control. The .gitignore already excludes it, but always verify before pushing.

ScreenScraper API

GameLord uses ScreenScraper for game metadata and cover art:
1

Register for an account

Visit screenscraper.fr and create a free account.
2

Request developer credentials

Post in the developer forum requesting a dev ID and password. Include your project name and purpose.
3

Add credentials to .env

Once approved, add your credentials:
SCREENSCRAPER_DEV_ID=gamelord
SCREENSCRAPER_DEV_PASSWORD=your_assigned_password
Without ScreenScraper credentials, artwork sync will fail. The app will still function, but games won’t have cover art or metadata.

Build-time variables

These are used during packaging and notarization:
# Apple Developer credentials for code signing and notarization
APPLE_DEVELOPER_ID="Developer ID Application: Your Name (TEAMID)"
APPLE_ID="[email protected]"
APPLE_ID_PASSWORD="app-specific-password"  # Generate at appleid.apple.com
APPLE_TEAM_ID="TEAMID"
Code signing variables are only needed for distribution builds (pnpm run publish). Development builds work without them.

User data directory

GameLord stores all runtime data in the Electron user data directory:
~/Library/Application Support/GameLord/

Directory structure

GameLord/
├── cores/                    # Downloaded libretro cores
│   ├── fceumm_libretro.dylib
│   ├── snes9x_libretro.dylib
│   └── ...
├── BIOS/                     # BIOS files for systems that require them
│   ├── sega_101.bin         # Saturn BIOS
│   └── mpr-17933.bin        # Saturn BIOS
├── save-states/              # Per-game save state directories
│   ├── SuperMario64/
│   │   ├── autosave.sav
│   │   ├── state-1.sav
│   │   └── state-2.sav
│   └── ...
├── sram/                     # Battery-backed saves (.srm files)
│   ├── SuperMario64.srm
│   └── ...
├── screenshots/              # Screenshots (raw RGBA format)
│   ├── screenshot-1234567890.raw
│   └── ...
├── roms-cache/              # Extracted ROMs from zip archives
│   ├── a1b2c3d4-rom.nes
│   └── ...
├── artwork/                  # Cached cover art from ScreenScraper
│   ├── 12345-boxart.jpg
│   └── ...
├── library.json             # Game library database
├── library-config.json      # Library scan paths and system definitions
└── logs/                    # Application logs
    ├── main.log
    └── renderer.log
The user data directory is created automatically on first launch. You can open it from the app via the Settings menu (planned feature).

Runtime configuration files

library-config.json

Stores library scan paths and system configurations:
{
  "systems": [
    {
      "id": "nes",
      "name": "Nintendo Entertainment System",
      "shortName": "NES",
      "manufacturer": "Nintendo",
      "extensions": [".nes", ".zip", ".7z"],
      "requiresBios": false
    }
    // ... more systems
  ],
  "romsBasePath": "/Users/you/ROMs",
  "scanRecursive": true,
  "autoScan": false
}
  • id: Unique identifier (e.g., nes, snes, genesis)
  • name: Full display name
  • shortName: Abbreviated name for UI space constraints
  • manufacturer: Console manufacturer
  • extensions: Supported file extensions (include .zip/.7z for compressed ROMs)
  • requiresBios: Whether the system needs BIOS files
  • biosFiles (optional): Array of required BIOS filenames

library.json

Game library database (managed by LibraryService):
{
  "games": [
    {
      "id": "sha256-hash-of-rom-content",
      "title": "Super Mario 64",
      "systemId": "n64",
      "romPath": "/Users/you/ROMs/N64/SuperMario64.z64",
      "romMtime": 1234567890123,
      "romHashes": {
        "crc32": "635a2bff",
        "sha1": "9bef1128717f958171a4afac3ed78ee2bb4e86ce",
        "md5": "20b854b239203baf6c961b850a4a51a2"
      },
      "coverArt": "artwork://12345",
      "metadata": {
        "releaseDate": "1996-06-23",
        "genre": "Platform",
        "publisher": "Nintendo",
        "developer": "Nintendo EAD",
        "players": 1,
        "rating": 0.95
      },
      "favorite": false,
      "playCount": 0,
      "totalPlaytime": 0
    }
  ]
}
Don’t edit library.json manually while the app is running - changes will be overwritten. Use the library UI or IPC handlers to modify game data.

Core configuration

Core paths

Cores are downloaded automatically to:
~/Library/Application Support/GameLord/cores/
Manual core installation:
1

Download core from buildbot

Visit buildbot.libretro.com and navigate to your platform:
  • macOS ARM64: apple/osx/arm64/latest/
  • macOS x86_64: apple/osx/x86_64/latest/
  • Linux x86_64: linux/x86_64/latest/
  • Windows x86_64: windows/x86_64/latest/
2

Extract the core

Unzip the downloaded .zip file. You’ll get a .dylib (macOS), .so (Linux), or .dll (Windows) file.
3

Place in cores directory

Move the core file to:
~/Library/Application Support/GameLord/cores/
Keep the original filename (e.g., fceumm_libretro.dylib).

BIOS files

Some systems (Saturn, PlayStation) require BIOS files:
Required files in GameLord/BIOS/:
  • sega_101.bin (Japanese BIOS)
  • mpr-17933.bin (US/EU BIOS)
BIOS files are copyrighted and cannot be distributed with GameLord. You must dump them from your own hardware or obtain them legally.
The BIOS/ directory is created automatically on first launch. Place BIOS files there and restart the app.

Logging

Logging is handled by electron-log:
import log from 'electron-log';

log.info('Application started');
log.warn('Something unexpected happened');
log.error('An error occurred', error);

Log locations

~/Library/Logs/GameLord/main.log
~/Library/Logs/GameLord/renderer.log

Log levels

// Set log level (default: 'info' in prod, 'debug' in dev)
log.transports.file.level = 'debug';
log.transports.console.level = 'debug';

// Available levels: error, warn, info, verbose, debug, silly
Development:
# Main process logs go to terminal
pnpm start

# Renderer logs visible in DevTools Console (Cmd+Option+I)
Production:
# Tail main process log
tail -f ~/Library/Logs/GameLord/main.log

# Tail renderer log
tail -f ~/Library/Logs/GameLord/renderer.log

Performance tuning

Frame pacing

Adjust the spin threshold for frame timing (in core-worker.ts):
// Lower = more precise timing, higher CPU usage
// Higher = lower CPU usage, more jitter
const SPIN_THRESHOLD_MS = 2; // Default: 2ms

Audio buffer size

Adjust the audio ring buffer size (in libretro_core.cc):
// Default: 16384 samples (power-of-2)
// Larger = more buffering, less underruns
// Smaller = lower latency, more risk of underruns
static const size_t AUDIO_BUFFER_SIZE = 16384;

Hash concurrency

Adjust parallel hashing during library scans (in LibraryService.ts):
// Number of ROMs to hash concurrently
const HASH_CONCURRENCY = 4; // Default: 4
Changing these values requires rebuilding from source. They’re not exposed in the UI (yet).

Development mode features

Git branch indicator

The dev build injects git context as compile-time constants:
// Available in renderer during development
const branch = __DEV_GIT_BRANCH__;        // "feat/new-feature"
const worktree = __DEV_WORKTREE_NAME__;   // "worktree-name"
const path = __DEV_WORKTREE_PATH__;       // "/path/to/worktree"
These are displayed in the UI to help identify which branch/worktree you’re testing.

Hot reload

Main process and workers rebuild on file changes, but require a full restart. Renderer uses React Fast Refresh for instant updates.
If hot reload stops working, restart the dev server: Ctrl+C then pnpm start

Next steps

Architecture

Understand the multi-process architecture and data flow

Libretro cores

Learn about core integration, system support, and BIOS requirements

Build docs developers (and LLMs) love