NSF player

@xromdev/nsf v0.1.0 · MIT

An NSF file is a game's music with the game taken away: the music driver's code and its data. This library plays one the way the console did — the driver runs on an emulated 6502, sixty times a second, and every write it makes lands in the sound chip at the cycle it was made. Nothing here knows how any game stores its music; the file's own code does.

NSF: the game’s own driver runs
any
NSF: the game’s own driver runs
opcodes, undocumented ones too
256
opcodes, undocumented ones too
faster than real time
~150×
faster than real time
write, at the cycle it was made
every
write, at the cycle it was made

xrom demo tune

xrom.dev2026 xrom.dev, MIT
File
xrom-demo.nsf · NSF v1
Region
NTSC · PLAY 60.10 times a second
Load / Init / Play
$8000 / $8000 / $8010
Bankswitching
no
Extra sound chips
none — the 2A03 alone
or drop one anywhere on this panelFiles stay in your browser — nothing is uploaded.
0:00
Output — the five channels through the mixer and the console's filters
Pulse 1
Level 0–15
Pulse 2
Level 0–15
Triangle
Level 0–15
Noise
Level 0–15
DMC
Level 0–127

What the driver writes

Everything the music does reaches the chip as these writes. Newest on top.

  1. press Play

Use it

Offline: a file and a song number in, samples and the driver's writes out. The button runs this code on the file above, here in your browser.

import { parseNsf, renderNsf } from '@xromdev/nsf';

const file = new Uint8Array(await (await fetch('/xrom-demo.nsf')).arrayBuffer());

const info = parseNsf(file);
// → "xrom demo tune" by xrom.dev, 1 song, NTSC"

// 30 seconds of song 1, and every register write the driver made.
const { samples, sampleRate, writes } = renderNsf(file, 1, 30);

// writes: [{ time, address, value }, …] — replay them with @xromdev/apu:
//   renderWrites(writes, 30)