JSNES
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.
Runs on: Web browser
JSNES Online Emulator
Play JSNES using JavaScript directly in your browser.
Controls
Configurations
| Configuration | Emulator | Machine | OS | Legal | |
|---|---|---|---|---|---|
| Nova the Squirrel | JSNES | Nintendo NES | open | Open ⛶ | |
| Nova the Squirrel | JSNES | Nintendo Famicom | open | Open ⛶ |
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:
| Member | Kind | What it does |
|---|---|---|
loadROM(data) | method | Load a ROM from an iNES .nes binary string and set up its mapper. |
frame() | method | Emulate one video frame — steps the CPU, PPU and APU, then fires onFrame. |
reset() | method | Reset the machine, like the console's reset button. |
reloadROM() | method | Reload the currently-loaded ROM from scratch. |
buttonDown(pad, btn) | method | Press 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) | method | Release a button. |
zapperMove(x, y) | method | Move the Zapper light-gun to screen coordinates. |
zapperFireDown(), zapperFireUp() | method | Pull and release the Zapper trigger. |
getFPS() | method | The current emulation frame rate. |
setFramerate(rate) | method | Set the target frame rate (recomputes frame timing and audio). |
stop() | method | Stop the audio processing unit. |
toJSON() | method | Serialise the full machine state — a save state. |
fromJSON(state) | method | Restore machine state produced by toJSON(). |
cpu | field | The 6502 CPU; cpu.mem is the 64 KB address space, readable and pokeable live (game state, cheats, a debugger). |
ppu | field | The Picture Processing Unit — nametables, sprites and palettes. |
papu | field | The audio processing unit (APU). |
mmap | field | The active cartridge mapper, created by loadROM. |
controllers | field | State of the two gamepads ({1: […], 2: […]}). |
opts | field | Merged options: onFrame, onAudioSample, onStatusUpdate, onBatteryRamWrite, preferredFrameRate, emulateSound, sampleRate. |
romData | field | The raw ROM data currently loaded. |
frameTime | field | Milliseconds per frame, derived from preferredFrameRate. |
fpsFrameCount | field | Internal frame counter behind getFPS(). |
ui | field | Internal 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 atcpu.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 toonFrame.PAPU— the audio unit, synthesising the two pulse, triangle, noise and DPCM channels into samples foronAudioSample.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.
Sound
JSNES already emulates the full Ricoh 2A03 audio unit (papu) — the two pulse channels, triangle, noise and DPCM — so the embed just carries its output to the shared EmuAudio sink; no sound chip had to be written. The core is constructed at sampleRate: EmuAudio.sampleRate, so the APU synthesises samples at the audio device's own rate and needs no resampling (and plays at the correct pitch).
The machine fires an onAudioSample(l, r) callback per generated sample, each channel a float in [-1, 1]. The boot script clamps those to Int16, accumulates the interleaved stereo pairs, and once per video frame — from inside the same per-frame tick() the debugger's run-loop already owns — pushes exactly Math.round(EmuAudio.sampleRate / 60) stereo samples to the sink, keeping any surplus for the next frame and zero-padding on underrun:
var nes = new jsnes.NES({
onAudioSample: onSample, // buffer clamped L,R Int16 pairs
sampleRate: EmuAudio.sampleRate, // APU runs at the sink rate
emulateSound: true
});
function tick(){ nes.frame(); render(); pushAudio(); } // exactly rate/60 stereo samples per frame
The transport exposes setMute / isMuted, which delegate straight to EmuAudio and drive the shell's Sound button. Audio starts muted (browsers block sound before a user gesture) and unmutes on the first real click. Note that the default title screen is silent until you press Start and the in-game music begins.