Performance

The two settings.json keys that set the V8 heap-size switch and extra Chromium switches, the system info a mod can read, and large-map fixes.

memoryLimitMB and chromiumFlags in settings.json are read at launch by the main process. Both need a restart; neither has a settings screen.

Settings File Location#

OSPath
macOS~/Library/Application Support/metro-maker4/settings.json
Windows%APPDATA%\metro-maker4\settings.json
Linux~/.config/metro-maker4/settings.json

It is the folder that holds mods/. The file is plain JSON; the game writes its own keys (language, enabledMods, ...) to it, so edit it with the game closed.

Memory Limit#

memoryLimitMB sets the V8 heap limit, passed to Chromium as --max-old-space-size. Without the key it is max(2048, floor((total RAM in GB - 2) * 1024)), capped at 12288 on Windows.

json
{
    "memoryLimitMB": 16384
}

A value of 1024 or more replaces the automatic limit, with no Windows cap. A value under 1024 is ignored and the automatic limit applies.

Chromium Flags#

chromiumFlags is an array of Chromium or V8 switches, appended after the game's own switches.

json
{
    "chromiumFlags": ["--disable-gpu", "--enable-logging"]
}

A leading -- is stripped. name=value is split into switch and value.

Verifying the Settings#

The startup log is main-process output: it is in the terminal that launched the app and in logs/metro-maker-current.log inside the saves folder (the folder Debugging describes: outside Steam builds a custom saves folder or Documents/SubwayBuilder/saves/; on Steam builds SubwayBuilder/saves/ under %APPDATA%, ~/Library/Application Support or $XDG_DATA_HOME, default ~/.local/share), and absent from DevTools. Settings > Advanced > Export Logs downloads the current log file and, when present, the previous one as one .log.

[Perf] System: 32GB RAM, 16 cores - heap limit: 16.0GB (user override)
[Flags] Applied 2 user flag(s): --disable-gpu, --enable-logging

(auto) in place of (user override) means memoryLimitMB was missing, 0 or another falsy value: check the file path and the JSON. A value from 1 to 1023 or a negative value is ignored, but the line still says (user override); check the heap limit it prints. The [Flags] line is absent when chromiumFlags is missing or empty.

Reading System Info from a Mod#

window.electron.getSystemPerformanceInfo() belongs to the desktop bridge (window.electron); window.SubwayBuilderAPI has no equivalent. It resolves to the values the main process computed at launch.

js
const info = await window.electron?.getSystemPerformanceInfo?.();
if (info) {
    console.log(`Heap: ${info.heapSizeMB} MB (override: ${info.heapSizeIsUserOverride})`);
    console.log(`RAM: ${info.totalRAMGB} GB, cores: ${info.cpuCores}`);
}
FieldTypeDescription
totalRAMGBnumberTotal system RAM in GB, rounded to an integer
cpuCoresnumberLogical CPU count (os.cpus().length)
heapSizeMBnumberHeap limit passed to V8 at launch; MB
heapSizeIsUserOverridebooleanTrue when memoryLimitMB holds any truthy value, including an ignored one under 1024
platformstringprocess.platform: 'darwin', 'win32' or 'linux'
archstringprocess.arch: 'x64', 'arm64', ...

The game's TypeScript declaration for the bridge lists only totalRAMGB and cpuCores; the resolved object carries all six fields.

Common Large-Map Issues#

SymptomLikely causeFix
The app crashes or goes blank on a large cityRenderer out of memoryRaise memoryLimitMB, restart
Map tiles stop loadingMemory pressureRaise memoryLimitMB
The game slows down over a long sessionGarbage collection near the heap limitSet memoryLimitMB 2-4 GB above what the city needs
The [Perf] line shows the expected heap and memory still runs outNative or WASM memory, outside the V8 heapReport it with the [Perf] line and the log file
On Windows the automatic heap stops at 12 GBThe automatic limit is capped on WindowsSet memoryLimitMB explicitly

DevTools at Launch#

Packaged builds open DevTools at startup when the DEBUG_PROD environment variable is set or the --devtools launch flag is passed. F12 toggles DevTools in every build.

Per-Mod Runtime Cost#

Every call the game makes into a mod's code (a hook callback, a registered UI component, the immediate onGameLoaded call) is timed. Every 5 real minutes, and on save/quit, the log gets one line:

[ModPerf] my-mod: 1.23ms/s (40 calls, max 5.10ms) | other-mod: 0.02ms/s (2 calls, max 0.90ms)

ms/s is milliseconds of wall time spent in that mod per second of real time (not game time, so it doesn't change with game speed), sorted worst first. Nothing is logged when no mods are loaded. The MOD_PERF_WARNINGS flag (off by default) adds a one-time toast per mod per session when, over a 60-second window, a mod averages more than ~4ms/s or has a single call over 100ms; turning it on doesn't change what's tracked, only whether a toast fires.

A mod's own onWarning and onTick-style handlers are worth keeping cheap: onWarning can fire once per flagged station every 30 game-seconds, so a handler that does real work per call adds up across a city with several warned stations. If [ModPerf] shows a mod costing real time, trim what its hook callbacks and UI components do per call rather than how often they're registered.