Nostalgist.js: launching a RetroArch core from a few lines of JavaScript
A JavaScript library used for running emulators of retro consoles inside browsers.
At a glance
- What is it?
- Nostalgist.js is a TypeScript library that runs RetroArch emulator cores in a browser, so a ROM and a core are enough to start a game, with save states, config overrides and access to the Emscripten filesystem underneath. It suits a developer building an emulator in a web app, and the ROMs and cores are loaded from a public CDN by default.
- Who is it for?
- Adopt Nostalgist.js when you need to run a console emulator inside a web application, because it removes the awkward part of RetroArch in a browser, namely that launching it programmatically is not straightforward, and gives you a handful of calls instead. Do not adopt it if you want a playable game rather than a library, since the README points players to a different project, RetroAssembly.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 13 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The gap it fills, and what it is built on
Nostalgist.js is a JavaScript library that allows you to run emulators of retro consoles, like NES and Sega Genesis, within web browsers. The primary language is TypeScript, the licence is MIT, and the homepage is nostalgist.js.org.
The motivation section is the clearest statement of the problem. Nostalgist.js is built on top of RetroArch Emscripten builds, and the authors love RetroArch in browsers because it is portable and convenient. Although there is already an official instance, the RetroArch web player, and some third-party ones like webretro, it is still not that easy to launch RetroArch in a browser programmatically.
So the purpose is stated narrowly: to simplify the process of launching an emulator to play a game, via RetroArch, in browsers. Given a ROM and a core, the game should be launched without any additional configuration.
That is a small promise and a real one. RetroArch itself is a frontend, and the emulators are the cores, and the WebAssembly builds of RetroArch are what make the whole thing possible in a browser at all. What Nostalgist.js adds is the loading, the core selection and the state handling, so that a developer does not have to assemble the Emscripten module, the core and the ROM themselves.
The topics attached to the repository list the consoles and the technologies together, which is a fair map of the scope: atari and atari2600, gb, gbc, gba, megadrive, nes, snes, virtualboy, and the platform names Sega and Nintendo, alongside emscripten, retroarch, retroarch-wasm, retroarch-web and webassembly.
Launching a game, and the shape of the API
The entry point is a launch call with a core and a ROM:
import { Nostalgist } from 'nostalgist'
await Nostalgist.launch({
core: 'fceumm',
rom: 'flappybird.nes',
})The core name is a libretro core identifier, fceumm being the NES one, and the ROM is a path or URL.
There is also a per-console shorthand, and the second feature example uses it:
import { Nostalgist } from 'nostalgist'
const nostalgist = await Nostalgist.nes('flappybird.nes')
const { state } = await nostalgist.saveState()
await nostalgist.loadState(state)So Nostalgist.nes takes a ROM and returns an instance, and saveState and loadState take and return a state object. The fact that state survives a save and a load is the whole point of the pair, and the destructuring of state out of the result tells you the return value carries more than the state itself.
The API surface is documented per method at nostalgist.js.org/apis, with the README linking the launch page, the save state and load state pages, and the two Emscripten accessors.
The library is published on npm as nostalgist, and the package manifest shows the distribution shape. It is an ES module, with a main entry of ./dist/nostalgist.umd.js, a module entry of ./dist/nostalgist.js, types at ./dist/nostalgist.d.ts, and an exports map that resolves the import and require conditions separately. The only published file is dist, and the runtime dependencies are three type packages, for emscripten, ini and path-browserify, which tells you the library itself is mostly types plus the loading logic.
Config overrides, so the core is not a black box
The third feature is the one that separates a wrapper from a usable tool: you can change any RetroArch configuration before launching.
import { Nostalgist } from 'nostalgist'
const nostalgist = await Nostalgist.launch({
core: 'fceumm',
retroarchConfig: {
rewind_enable: true,
},
retroarchCoreConfig: {
fceumm_turbo_enable: 'Both',
},
rom: 'flappybird.nes',
})There are two configuration objects and the distinction is meaningful. retroarchConfig holds settings for the RetroArch frontend itself, and rewind_enable, which turns on the rewind feature, is one of those. retroarchCoreConfig holds settings for the specific core, and fceumm_turbo_enable with the value Both is a core-level turbo mode setting.
That separation matters because the two live in different places. A frontend setting and a core setting are not the same kind of thing, and a library that collapsed them into one bag would make it unclear which knob you were turning. Keeping them apart means the same launch call can configure both layers and you still know which is which.
The important caveat is what these keys mean. They are RetroArch and libretro option names, so they depend on the version of the core you load. The set of available options is therefore a property of the core, not of the library, and the library's job is to pass them through in the right place.
It is also worth noting what the wording does not claim. The library is not offering a curated set of friendly options, it is offering the raw configuration surface, and that is the more useful thing for a developer who already knows what they want to change.
Getting at the Emscripten filesystem underneath
The fourth feature is the escape hatch, and it is the one that makes the library composable with other tools.
import { Nostalgist } from 'nostalgist'
const rom = 'https://example.com/zelda.sfc'
const nostalgist = await Nostalgist.snes(rom)
const FS = nostalgist.getEmscriptenFS()
FS.readdir('/')getEmscriptenFS returns the Emscripten filesystem object, and from there you can read a directory. The example lists the root of the emulated filesystem, which for a SNES game means looking at the files the game wrote or expects to find.
This is a small API with large consequences. Once you have the Emscripten filesystem, you can read and write the emulated disk, which means save files can be extracted and restored, a game can be given a file it expects, and the state of the emulated machine can be inspected. The related API list includes getEmscriptenModule as well as getEmscriptenFS, so the raw module is reachable too.
The design philosophy is consistent with the rest of the project. The library does not hide the machinery, it makes the machinery reachable. The save state API is a convenience on top of that filesystem, the config override is a convenience on top of RetroArch, and when a convenience does not exist you can drop down to Emscripten directly.
For a developer building something like a game browser, a collection cabinet, or a tool that inspects ROM behaviour, that layering is the point.
Where the ROMs and cores come from, and who this is for
The Credits section is short and has two practical points in it.
First, the fundamental dependencies. RetroArch and the emulators or cores that go with it, and Emscripten. Nostalgist.js is not an emulator; it is a way to run the RetroArch WebAssembly build and its cores, which is why it inherits RetroArch's compatibility and its quirks.
Second, the ROMs. The README says ROMs are being used for the purpose of demonstration, with retrobrews and the authors of the homebrew games named, and that by default the ROMs and cores are all loaded from a free CDN service, jsDelivr.
That default is convenient and it is also the thing to think about before shipping. A CDN for cores and homebrew ROMs is fine for a demo. It is a different question for a product with your own content, and for a hosted service, because you are depending on a third party's availability and their terms for the content your users are loading.
The README also states who the library is for. It is for developers, and if you are a player rather than a developer planning to build something, the recommendation is to try another open-source project, RetroAssembly, which is by the same author and is a personal retro game collection cabinet in the browser.
The list of projects built on it gives a sense of the range. RetroAssembly, vme as a multi emulator web app, ultimate-homebrew-extensions which runs an emulator to test ROMs inside VS Code, NextCloud Nostalgist which runs emulators directly in NextCloud, Nostalbit built with Next.js, Tailwind and TypeScript, AstroEmu for Famicom and Super Famicom on the web, Koin.js as a preservation-focused Netflix style emulation engine, and Foxol as a platform with multiplayer and cloud saves. The alternative named explicitly is EmulatorJS, described as another JavaScript library for emulation inside browsers, more powerful and more sophisticated, with many custom cores and a friendly user interface.
That is the honest comparison to keep in mind. If you want a finished player rather than a library, those projects are further along.
Editorial conclusion
Adopt Nostalgist.js when you need to run a console emulator inside a web application, because it removes the awkward part of RetroArch in a browser, namely that launching it programmatically is not straightforward, and gives you a handful of calls instead. Do not adopt it if you want a playable game rather than a library, since the README points players to a different project, RetroAssembly. Before you ship anything, check the ROM and core hosting, because by default both are loaded from a free CDN, jsDelivr, and the demo ROMs come from retrobrews, so you need to know whether your own content is allowed to live there or belongs on your own server.
Frequently asked questions
How do I launch a retro console emulator in the browser with Nostalgist.js?
Import Nostalgist and call launch with a core and a ROM, for example `await Nostalgist.launch({ core: 'fceumm', rom: 'flappybird.nes' })`. There is also a per-console shorthand such as `Nostalgist.nes('flappybird.nes')` that returns an instance you can call methods on.
Can I change RetroArch settings when launching with Nostalgist.js?
Yes, and there are two separate objects for it. retroarchConfig sets frontend options such as rewind_enable, and retroarchCoreConfig sets options of the specific core such as fceumm_turbo_enable. These are the raw RetroArch and libretro option names, so which ones exist depends on the core version you load.
How do I save and load game state in Nostalgist.js?
Call saveState on the instance to get an object containing the state, and pass it back to loadState. The library also exposes the Emscripten filesystem through getEmscriptenFS, and the raw module through getEmscriptenModule, so you can read and write the emulated disk directly.
Where do Nostalgist.js ROMs and cores come from?
By default all ROMs and cores are loaded from the free jsDelivr CDN, and the demo ROMs come from retrobrews and the authors of the homebrew games. The underlying dependencies are RetroArch with its cores, built to run under Emscripten, and the library itself is MIT licensed and published on npm as nostalgist.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/arianrhodsandlot-nostalgist)Was this useful?