emame aims to provide a general web environment for running anything supported by MAME. The current pages and CI build cover a selected set of machines; broad MAME coverage is the goal. Emularity is a predecessor project for browser-based emulation.
The repository contains the driver pages, browser loader/theme, per-machine configuration, and a CI workflow that builds MAME master merged with the emame-wip branch.
- Machine configurations live in
machines.json; the universalrun.html?machine=<key>page renders any entry, andindex.htmllinks to them (sections, order and labels are curated there). mame-loader.jsloads MAME and exposes the browser bridge;theme-simple.jsowns the page UI.cfg/andnvram/contain seeded per-machine state.roms/andsoftware/are ignored, user-supplied directories. ROMs and software are not included.RESOLUTION.mddocuments the per-driver display-size policy.
Use an Emscripten SDK and a MAME checkout on emame-wip. MAME_SRC defaults to ../mame/ relative to this repository; set it to another MAME source checkout when needed. EMSDK_DIR defaults to ~/workspace/emsdk.
EMSDK_DIR="$HOME/workspace/emsdk" MAME_SRC="$HOME/workspace/mame" ./build.shbuild.sh compiles the sources in MAME_SRC but keeps objects, generated sources, and genie projects in this repository's build/ (BUILDDIR). SEPARATE_BIN=1 puts the linked Emscripten target in build/asmjs/bin/; the script copies mame.html, mame.js, and mame.wasm to this web root for serving. A native build in the MAME checkout uses its own build/, so the two build trees do not clobber each other or force recompiles.
build.sh refuses to run unless MAME_SRC is on the emame-wip branch; pass --skip-branch-check to build from another branch anyway.
BUILDDIR must stay relative to the MAME checkout: genie composes MAME_DIR .. BUILDDIR, so an absolute path generates the makefiles into a bogus tree and the build dies on a missing project file.
start-web.sh starts Python's built-in static HTTP server from the emame directory on port 8080. After building, run ./start-web.sh and open http://localhost:8080/index.html (or a machine page such as run.html?machine=tbblue) in a browser. Stop the server with Ctrl+C. ROMs and software must be supplied separately in the ignored roms/ and software/ directories.
mame-js runs the Emscripten build on the desktop through node — no browser, no native build. It picks the newest node from $EMSDK_DIR (falling back to node on PATH); the bundle requires node ≥ 18.3. The launcher (mame-node.mjs) stubs the window object SDL probes at startup and maps host paths into the emscripten filesystem: ROM zips are preloaded from roms/ by default, and any file/dir argument that exists on the host (-rompath, -autoboot_script, -*_directory) is copied or created at the same path inside MEMFS. Use --no-rom-preload to skip the default.
./mame-js -listxml galaxian # driver info from the shipped binary
./mame-js -verifyroms frogger # ROM presence + checksum check
for d in frogger pacman dkong galaxian; do ./mame-js -verifyroms $d || echo "MISSING: $d"; doneA single-set -verifyroms exits nonzero when the set is missing or bad — that is the reliable ROM-gate form; multi-set invocations skip missing sets silently and may exit zero. Machines also run headless (-video none -str N, -autoboot_script): SDL additionally probes screen and document during init, and the launcher stubs those the same way. This is why validate.py is js-first — a native checkout is usually a stale subset of the driver list.
validate.py checks the resolution field of each machines.json entry against frame sizes derived from MAME's -listxml data and the driver's boot-default view. With no arguments it checks every machine; pass machine slugs to limit the check. It searches integer-scaled frames within an 800x900 box, choosing the frame aspect closest to the effective view aspect and preferring the smaller frame on ties, so base dimensions remain the suggestion and configured integer upscales appear as xM in current. The taller box allows rotated arcade frames such as Frogger's 768x896 candidate; the old 800x600 cap excluded it. The current column prints OK for an exact match, xM for a uniform scale of the suggestion (for example, x2 or x1.5), or the actual resolution when it matches neither.
Rows are printed and flushed as each driver check completes rather than buffered until all drivers finish.
python3 validate.py # check all machines
python3 validate.py tbblue tsconf2-img # check selected machines
python3 validate.py --views tbblue # list the driver's views/defaultThe --views mode needs complete ROMs for each selected driver and exits nonzero when a driver cannot run. With no driver arguments, it lists views for all configured drivers.
- Add the driver file(s) to
SOURCESin bothbuild.shand the CI workflow; rebuild. - ROM zips go to
roms/(ignored, supplied per host). - Add a
machines.jsonentry. The key is the URL slug;driveronly when it differs. Conventions:romslists bare set names (parents and shared ROMs included, e.g.kb_ms_natural);mediavalues live undersoftware/and mount at their basename;cfg: truemeanscfg/<driver>.cfg.nvramstays explicit{url, path}— sources must be web-cache-unique (same file name fetched from different URLs serves the wrong demo after a cache hit) and may be renamed on the way in, so no convention is possible. python3 validate.py <slug>— noMISSINGlines, andcurrentprintsOKor an integer multiple (x2).- Link
run.html?machine=<slug>fromindex.html.
The emame web-front source is licensed under BSD-3-Clause; see LICENSE. CI artifacts also contain MAME binaries. MAME's top-level COPYING states that MAME as a whole is GPL-2.0, with individual files potentially under less restrictive licenses. The artifact ships that file as MAME-COPYING and the full docs/legal/ texts as MAME-LICENSES/. ROMs and software are user-supplied and may have separate terms.
emame-wip is kept rebased on mame/master; the CI merge step is only a fallback and intentionally fails on conflicts, signalling that a rebase is due.
running_machinebrowser bridges (emscripten_set_bgfx_chain,emscripten_resize_window,emscripten_set_keepaspect,emscripten_set_fastforward, andemscripten_cassette_toggle) plus the cassette tape-end watch.running_machine::emscripten_set_pausedmachine pause for the web pages, with the audio sink told about deliberate drain (no false underruns while paused).scripts/src/3rdparty.luatakes the asmjs toolchain down the clang path of its warning flags.- Wall-clock master run-loop pacing (MAME's internal throttle stays off) and CPU-budget unthrottled fast-forward.
- Runtime bgfx chain switching through
osd_common_t::set_bgfx_screen_chain,renderer_bgfx::set_effect_chain,chain_manager::set_chain_by_name, andosd_renderer::set_effect_chain. - Emscripten
EXPORTED_FUNCTIONSadditions andemscripten_post.jscwrap bindings deferred until first call. infoxml.cppruns-listxmltasks withstd::launch::deferredon Emscripten (no pthreads; the defaultstd::launch::asyncthrows), keeping headless-listxmlworking under node.emscripten_post.jsMEMFS preload hook (JSMAME.preload) for the headless node CLI.- Emscripten buildability updates in
scripts/src/osd/modules.luaandsrc/lib/util/chd.cpp. js_sound.jsunderrun padding uses the last played sample (silence before the first sample), avoiding NaNs.
- mamedev/mame#15666 — broader
js_soundoutput-delay changes. The underrun-padding fix is already carried onemame-wip; reconcile that overlapping code when the PR lands inmame/master. - mamedev/mame#16269 — run-loop pacing and unthrottled fast-forward, currently carried on
emame-wip; reconcile the duplicate when the PR lands inmame/master.
The CI merge intentionally fails on conflicts, signaling that emame-wip needs reconciliation with the updated upstream tree.