SearchA-ZJ › JSNES

JSNES

2010 Open source · GPL-3.0 Online

JSNES is a Nintendo Entertainment System emulator written entirely in JavaScript by Ben Firshman, running in-browser with an HTML5 canvas and Web Audio output. Its portable core has been reused in many web-based and Node.js NES projects.

Visit the official site ↗

Runs on: Web browser

JSNES Online Emulator

Play JSNES using JavaScript directly in your browser.

Configurations

ConfigurationEmulatorMachineOSLegal
Nova the SquirrelJSNESNintendo NESopenOpen ⛶
Nova the SquirrelJSNESNintendo FamicomopenOpen ⛶

Machines emulated

Chips

Notes

Embedding

JSNES is a pure-JavaScript library. Vendor jsnes.min.js and drive it from a short script that renders each frame to a <canvas>.

Boot and load a ROM. Construct the machine with an onFrame callback, then hand it the ROM as a binary string:

const nes = new jsnes.NES({
  onFrame: fb => { /* fb = 256×240 Int32 pixels, 0x00BBGGRR */ },
  onAudioSample: (l, r) => { /* push into a Web Audio buffer */ }
});
const buf = await (await fetch(romUrl)).arrayBuffer();
let bin = ''; new Uint8Array(buf).forEach(b => bin += String.fromCharCode(b));
nes.loadROM(bin);                       // loadROM wants a binary string
(function loop(){ nes.frame(); requestAnimationFrame(loop); })();

The NES object. Once constructed, nes exposes the whole machine as ordinary methods and properties, so a host page can do far more than render:

MemberKindWhat it does
loadROM(data)methodLoad a ROM from an iNES .nes binary string and set up its mapper.
frame()methodEmulate one video frame, steps the CPU, PPU and APU, then fires onFrame.
reset()methodReset the machine, like the console's reset button.
reloadROM()methodReload the currently-loaded ROM from scratch.
buttonDown(pad, btn)methodPress a button - pad is 1 or 2, btn is a jsnes.Controller.BUTTON_* (A, B, SELECT, START, UP, DOWN, LEFT, RIGHT). What an on-screen gamepad calls.
buttonUp(pad, btn)methodRelease a button.
zapperMove(x, y)methodMove the Zapper light-gun to screen coordinates.
zapperFireDown(), zapperFireUp()methodPull and release the Zapper trigger.
getFPS()methodThe current emulation frame rate.
setFramerate(rate)methodSet the target frame rate (recomputes frame timing and audio).
stop()methodStop the audio processing unit.
toJSON()methodSerialise the full machine state, a save state.
fromJSON(state)methodRestore machine state produced by toJSON().
cpufieldThe 6502 CPU; cpu.mem is the 64 KB address space, readable and pokeable live (game state, cheats, a debugger).
ppufieldThe Picture Processing Unit, nametables, sprites and palettes.
papufieldThe audio processing unit (APU).
mmapfieldThe active cartridge mapper, created by loadROM.
controllersfieldState of the two gamepads ({1: […], 2: […]}).
optsfieldMerged options: onFrame, onAudioSample, onStatusUpdate, onBatteryRamWrite, preferredFrameRate, emulateSound, sampleRate.
romDatafieldThe raw ROM data currently loaded.
frameTimefieldMilliseconds per frame, derived from preferredFrameRate.
fpsFrameCountfieldInternal frame counter behind getFPS().
uifieldInternal binding of the frame and status callbacks.

Because it is all JavaScript, the machine is fully inspectable and controllable at runtime, straight from the host page.

Architecture

JSNES is a readable, interpreted NES emulator, a JavaScript descendant of the vNES emulator (originally Java). Each hardware block is its own object hanging off the top-level NES:

  • CPU - an interpreter of the Ricoh 2A03's 6502 core, with the 64 KB memory map at cpu.mem.
  • PPU - the Picture Processing Unit: it draws the background and up to 64 sprites into a 256×240 32-bit framebuffer, delivered once per frame to onFrame.
  • PAPU - the audio unit, synthesising the two pulse, triangle, noise and DPCM channels into samples for onAudioSample.
  • Mappers - one class per cartridge mapper (NROM, MMC1, MMC3, …) implementing bank-switching; the iNES header selects which.
  • ROM - parses the iNES header and holds the PRG and CHR banks.
  • Controller - the two gamepads and their button latches.

Each nes.frame() runs the CPU for one video frame's worth of cycles while stepping the PPU and APU in lockstep, then emits the finished framebuffer. Because the components are ordinary objects, the whole emulator state is inspectable and serialisable, which is how the toJSON/fromJSON save states work.