MCPcopy
hub / github.com/bfirsh/jsnes

github.com/bfirsh/jsnes @v2.1.0 sqlite

repository ↗ · DeepWiki ↗ · release v2.1.0 ↗
544 symbols 1,180 edges 75 files 6 documented · 1%
README

JSNES

A JavaScript NES emulator.

It's a library that works in both the browser and Node.js.

Installation

For Node.js or Webpack:

$ npm install jsnes

In the browser, you can use unpkg:

<script type="text/javascript" src="https://unpkg.com/jsnes@2/dist/jsnes.min.js"></script>

Usage

Browser

The easiest way to use JSNES in a web page is with jsnes.Browser. It handles canvas rendering, audio, keyboard input, gamepad input, and frame timing automatically.






<script src="https://unpkg.com/jsnes@2/dist/jsnes.min.js"></script>
<script>
  var browser = new jsnes.Browser({
    container: document.getElementById("nes"),
    onError: function (e) {
      console.error(e);
    },
  });
  jsnes.Browser.loadROMFromURL("my-rom.nes", function (err, data) {
    if (err) {
      console.error(err);
      return;
    }
    browser.loadROM(data);
  });
</script>

If you already have ROM data as a string or byte array, you can pass it directly:

var browser = new jsnes.Browser({
  container: document.getElementById("nes"),
  romData: romData,
});

See Keyboard Controls below for default key bindings. Gamepads are also supported automatically.

A complete embedding example is in the example/ directory. You can try it by running npx serve . in the repository root and opening http://localhost:3000/example/nes-embed.

React

import { Browser } from "jsnes";

function Emulator({ romData }) {
  const containerRef = useRef(null);
  const browserRef = useRef(null);

  useEffect(() => {
    browserRef.current = new Browser({
      container: containerRef.current,
      romData: romData,
    });
    return () => browserRef.current.destroy();
  }, [romData]);

  return 

;
}

A full-featured React frontend is available in the web/ directory of this repository.

Node.js / custom integration

If you need lower-level control (custom rendering, audio handling, or running in Node.js), use the NES class directly:

// Initialize and set up outputs
var nes = new jsnes.NES({
  onFrame: function(frameBuffer) {
    // ... write frameBuffer to screen
  },
  onAudioSample: function(left, right) {
    // ... play audio sample
  }
});

// Read ROM data from disk (using Node.js APIs, for the sake of this example)
const fs = require('fs');
var romData = fs.readFileSync('path/to/rom.nes', {encoding: 'binary'});

// Load ROM data as a string or byte array
nes.loadROM(romData);

// Run frames at 60 fps, or as fast as you can.
// You are responsible for reliable timing as best you can on your platform.
nes.frame();
nes.frame();
// ...

// Hook up whatever input device you have to the controller.
nes.buttonDown(1, jsnes.Controller.BUTTON_A);
nes.frame();
nes.buttonUp(1, jsnes.Controller.BUTTON_A);
nes.frame();
// ...

API Reference

NES

var nes = new jsnes.NES(options);

Options:

Option Type Description
onFrame function(frameBuffer) Called at the end of each frame with a 256×240 pixel buffer (Int32Array of ARGB values).
onAudioSample function(left, right) Called for each audio sample with left/right channel values (-1.0 to 1.0).
onStatusUpdate function(status) Called with status messages (e.g. "Ready to load a ROM.").
onBatteryRamWrite function(address, value) Called when battery-backed SRAM is written. Use this to persist save data.
emulateSound boolean Enable/disable audio emulation. Default: true.
sampleRate number Audio sample rate in Hz. Default: 48000.

Methods:

Method Description
nes.loadROM(data) Load a ROM from a string, Uint8Array, or ArrayBuffer.
nes.frame() Execute one frame of emulation. Call this at 60 fps.
nes.buttonDown(player, button) Press a button. player is 1 or 2.
nes.buttonUp(player, button) Release a button.
nes.reset() Reset the emulator (like pressing the reset button on the NES).
nes.reloadROM() Reload the current ROM from scratch.
nes.getFPS() Get the current frames-per-second count.
nes.setFramerate(rate) Change the target frame rate.
nes.toJSON() Serialize emulator state (for save states).
nes.fromJSON(data) Restore emulator state from a previous toJSON() call.
nes.zapperMove(x, y) Move the Zapper light gun to the given pixel coordinates.
nes.zapperFireDown() Pull the Zapper trigger.
nes.zapperFireUp() Release the Zapper trigger.

Button constants (on jsnes.Controller):

BUTTON_A, BUTTON_B, BUTTON_SELECT, BUTTON_START, BUTTON_UP, BUTTON_DOWN, BUTTON_LEFT, BUTTON_RIGHT, BUTTON_TURBO_A, BUTTON_TURBO_B

Browser

var browser = new jsnes.Browser(options);

Options:

Option Type Description
container HTMLElement Required. The DOM element to render into.
romData string ROM data to load immediately. If omitted, call loadROM() later.
onError function(error) Called when the emulator encounters an error during frame execution.
onBatteryRamWrite function(address, value) Called when battery-backed SRAM is written.

Methods:

Method Description
browser.start() Start emulation. Called automatically if romData was provided.
browser.stop() Pause emulation.
browser.loadROM(data) Load a new ROM and start emulation.
browser.fitInParent() Re-layout the canvas to fill its container.
browser.screenshot() Returns an HTMLImageElement of the current frame.
browser.destroy() Full cleanup: stop emulation, remove listeners, remove canvas.
Browser.loadROMFromURL(url, callback) Static method. Fetch ROM data from a URL. Callback is (error, data).

Properties:

Property Description
browser.nes The underlying NES instance.
browser.keyboard The KeyboardController for remapping keys.
browser.gamepad The GamepadController for remapping gamepad buttons.

Keyboard Controls

When using jsnes.Browser, the following keyboard bindings are set up by default:

Button Player 1 Player 2
Up / Down / Left / Right Arrow keys Numpad 8 / 2 / 4 / 6
A X Numpad 7
B Z (or Y) Numpad 9
Turbo A S
Turbo B A
Start Enter Numpad 1
Select Right Ctrl Numpad 3

Turbo A and Turbo B behave like A and B but auto-fire repeatedly while the key is held.

Key bindings can be customized at runtime via the browser.keyboard property. Use setKeys() to provide a custom mapping (persisted to localStorage) and loadKeys() to reload it:

// Get the current key map
var keys = browser.keyboard.keys;

// Remap Player 1 A button to the J key (keyCode 74)
keys[74] = [1, jsnes.Controller.BUTTON_A, "J"];
browser.keyboard.setKeys(keys);

Build

To build a distribution:

$ npm run build

This will create dist/jsnes.min.js.

Running tests

$ npm test

Formatting code

All code must conform to Prettier formatting. The test suite won't pass unless it does.

To automatically format all your code, run:

$ npm run format

Related projects

  • NEStation - Multiplayer NES over the internet!

Thanks

JSNES is based on James Sanders' vNES, and owes an awful lot to it. It also wouldn't have happened without Matt Wescott's JSSpeccy, which sparked the original idea. (Ben, circa 2008: "Hmm, I wonder what else could run in a browser?!")

Extension points exported contracts — how you extend this code

EmulatorData (Interface)
(no doc)
src/nes.d.ts
GameGeniePatch (Interface)
(no doc)
src/gamegenie.d.ts
BrowserOptions (Interface)
(no doc)
src/browser.d.ts
NESOptions (Interface)
(no doc)
src/nes.d.ts

Core symbols most depended-on inside this repo

write
called by 217
src/cpu.js
load
called by 152
src/cpu.js
getRgb
called by 65
src/ppu/palette-table.js
push
called by 27
src/cpu.js
loadRomBank
called by 27
src/mappers/mapper0.js
load1kVromBank
called by 27
src/mappers/mapper0.js
advanceDots
called by 24
src/ppu/index.js
loadROM
called by 20
src/nes.js

Shape

Method 384
Class 102
Function 54
Interface 4

Languages

TypeScript100%

Modules by API surface

src/ppu/index.js51 symbols
src/mappers/mapper0.js31 symbols
src/mappers/mapper5.js29 symbols
src/papu/index.js28 symbols
src/cpu.js27 symbols
test/cpu.spec.js22 symbols
src/gamegenie.js15 symbols
src/papu/channel-triangle.js14 symbols
src/ppu/palette-table.js13 symbols
src/papu/channel-square.js12 symbols
src/papu/channel-dm.js12 symbols
src/mappers/mapper1.js12 symbols

Dependencies from manifests, versioned

@eslint/js10.0.1 · 1×
@tailwindcss/vite4.2.2 · 1×
@testing-library/react16.3.2 · 1×
@types/node25.0.2 · 1×
@vitejs/plugin-react6.0.1 · 1×
eslint10.0.2 · 1×
eslint-config-prettier10.1.8 · 1×
eslint-webpack-plugin6.0.0 · 1×
jsdom29.0.2 · 1×
jsnesfile:.. · 1×
prettier3.6.2 · 1×

For agents

$ claude mcp add jsnes \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact