NES Emulator

nightowl-NES

A NES emulator, built from scratch in TypeScript. Zero runtime dependencies. Zero lines of human-written code.

Just the
↓
Play
Drop or select a .nes ROM to begin
1P Keyboard
D-Pad↑↓←→
A / BZX
Start / SelectEnterShift
2P Keyboard
D-PadWSAD
A / BJK
Start / SelectTG
Gamepad
D-PadD-Pad / Left Stick
AButton 2 (○/B)
BButton 1 (×/A)
StartButton 10 (Start/Menu)
SelectButton 9 (Select/Back)
Other
FullscreenF
Save (Slot 1~4)1~4
Load (Slot 1~4)Shift+1~4
📂
Drop a .nes file here
⚠️

Every opcode. Every scanline. Every channel.

12,616 lines of TypeScript. 1,572 tests. Zero runtime dependencies. Verified against the NES hardware specification.

CPU
0/256
216 of 256 opcodes — 151 official + 65 unofficial — implemented from the 6502 specification alone.
PPU
Full
Background + sprite rendering, loopy scroll registers, Sprite 0 Hit detection, scanline-accurate timing.
APU
0/5
All 5 channels — two Pulse, Triangle, Noise, DMC — with non-linear mixing.
Mappers
0
20 mappers covering ~85–90% of the commercial NES library.
✅
nestest
0
The gold-standard CPU verification ROM. 8,991 trace lines matched. Passed.
🛡️
Tests
0
1,572 pass / 6 skip / 0 fail. CI on every commit.
🎯
Assertions
0
217,919 expect() calls verifying correctness at opcode, register, and pixel level.
🎮
Test ROMs
0/263
66 of 263 ROMs from nes-test-roms. CPU 24 / PPU 16 / APU 12 / Mapper 6 / others 8. 197 still to go.
📝
Source
0
12,616 lines of TypeScript. Not a line more than needed.
✨
Dependencies
0
Zero runtime dependencies. TypeScript, Vite, and Vitest are dev-only.

Zero lines of human-written code.

Everything you just played was built by Claude Code — running a self-driving loop that executes the full GitHub Flow without human intervention. The emulator is what you see. The harness is why it exists.

The human just starts the loop. The machine does everything else.
0 merged PRs
0 Issues closed
0 days
NES: 68 PRs
Harness: 36 PRs

8 Days

The first four days were almost entirely about the harness. Getting an AI to run a sustainable development loop — branching, committing, opening PRs, reviewing its own code, merging, and picking up the next Issue — turned out to be the hardest part.

NES PRs Harness PRs
Day 1–2
12
13
CPU skeleton and first loop attempts. The harness crashed, lost context, couldn't recover. Broken PRs every morning.
Day 3
0 NES
5
Zero NES code. The entire day spent on the harness — surviving context resets, retrying from failures, automatic Issue pickup.
Day 4
7
13
The biggest rewrite: 13 harness PRs. Stop hooks, auto-recovery, bot reviewer identity, stuck protocol. The machinery for everything that followed.
Day 5–6
15
9
nestest trace passed. PPU rendering came alive. APU producing sound. The loop was starting to hold.
Day 7
26
5
26 NES issues closed in a single day. The harness worked — Issue → Branch → PR → Review → Merge, on repeat until the queue was empty.
Day 8
8
0 Harness
Zero harness PRs. Just NES. The loop ran all night without intervention.

The Self-Driving Loop

Shell scripts and event-driven hooks orchestrate the cycle: worker completes Issue → writes flag → Stop hook fires → loop-helper runs /clear → fresh session picks up next open Issue. Code review runs 7-angle finders under a bot identity. Stuck protocol drafts PRs back after 30 min. Auto-recover restarts from crashes.

Issue → Branch → PR → Review → Merge
repeat every night, autonomously

✓ Allowed References

  • nesdev.org wiki — NES hardware documentation
  • 6502 instruction set specification
  • iNES / NES 2.0 header format
  • nes-test-roms (submodule) — test ROMs

✗ Denied References

All existing NES emulator source code — 15 known repositories, any language, any license.

Pre-tool-use hooks block WebFetch URLs matching the deny list. A structural constraint, not an airtight seal — WebSearch results and local files are not blocked.