{"id":31068456,"url":"https://github.com/richstokes/nez","last_synced_at":"2026-08-16T12:32:07.300Z","repository":{"id":308091887,"uuid":"1031586722","full_name":"richstokes/NEZ","owner":"richstokes","description":"🕹️ Vibe Coding a NES emulator in python. 99% not working. Maybe one day it will!","archived":false,"fork":false,"pushed_at":"2026-02-13T05:04:36.000Z","size":1139,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-13T12:32:02.996Z","etag":null,"topics":["ai-experiments","emulation","nintendo","python-game-development","vibe-coding"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/richstokes.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-08-04T03:13:25.000Z","updated_at":"2026-02-13T05:04:40.000Z","dependencies_parsed_at":"2025-08-04T06:00:43.094Z","dependency_job_id":"540c2d20-f3f2-427d-95bb-ba934d246483","html_url":"https://github.com/richstokes/NEZ","commit_stats":null,"previous_names":["richstokes/nez"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/richstokes/NEZ","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/richstokes%2FNEZ","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/richstokes%2FNEZ/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/richstokes%2FNEZ/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/richstokes%2FNEZ/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/richstokes","download_url":"https://codeload.github.com/richstokes/NEZ/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/richstokes%2FNEZ/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":36714792,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-08-06T04:43:03.162Z","status":"online","status_checked_at":"2026-08-16T02:00:06.462Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-experiments","emulation","nintendo","python-game-development","vibe-coding"],"created_at":"2025-09-15T21:36:34.322Z","updated_at":"2026-08-16T12:32:07.281Z","avatar_url":"https://github.com/richstokes.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NEZ - NES Emulator\n\nA Nintendo Entertainment System (NES) emulator **vibe-coded** in Python.  \n\nAn experiment to see how close to a working emulator I can get, using both Warp and GitHub Copilot with various incantations of prompts and models. Not scientific at all, just a chance to play with the different models and get a feel for how they respond when pushed with challenging tasks.  \n\nI'm reviewing *none* of the generated code, instead I'm giving the LLMs feedback based on my experience when running the emulator and steering it on areas I think it may need to focus on.  \n\n### Updates\n\n#### August 2025\n\nRight now it can _kind_ of load some games/ROMs, but theres a ton of corruption and performance is terrible!\n\nI had assumed that being a 40 year old, incredibly well documented platform, that the LLMs may have been able to build this relatively easily. So far it's been a nightmare, but I'm stubborn so going to keep nudging this along.\n\n#### September 2025\n\nThis project was a horrible idea. LLMs are creating a mess. Python isn't fast enough. I will revisit this down the line and see if AI is at a point where this is less painful!\n\n#### February 2026\n\nRevisited with Opus 4.6. Had it review/update the codebase with a view to adding any missing or incomplete functions. I then had it add a headless mode, so that it could run the emulator itself and quickly gather stats. Eventually it profiled itself and we made the decision to move most of core logic to Cython (the pyx files) which has made a huge difference. I knew from the get-go that getting this to run on pure Python was a long shot, but this seems like a good compromise for now. Mario now runs at 60fps with no obvious issues.\n\nI think the most interesting takeaway so far, is that prior to this attempt I had a banner saying \"99% not working\" - it was terrible. Within one generation of LLM updates, I'd say thats reversed - its now 99% working. And all the annoying workflow issues I mention below are solved.\n\n## Screenshots\n\nFeb 2026, with latest Opus model, its basically working! Framerate isn't great but it's playable:  \n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"screenshots/c0.png\" alt=\"Screenshot of NEZ running\" width=\"50%\"\u003e\n\u003c/p\u003e\n\nEarlier/2025 screenshots:  \n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"screenshots/c1.png\" alt=\"Screenshot of NEZ running\" width=\"50%\"\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"screenshots/c2.png\" alt=\"Screenshot of NEZ running\" width=\"50%\"\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"screenshots/c3.png\" alt=\"Screenshot of NEZ running\" width=\"50%\"\u003e\n\u003c/p\u003e\n\n\n\n## Things I have found are not great when attempting this (mid-2025)\n\n- Can blow through a months Warp quota in a couple of hours when asking it to dive deep into implementing/reviewing logs. Copilot isn't much better. This experiment is doing a lot of iterating/scanning huge log output so perhaps understandable, but it doesn't feel like you get many credits for your money.\n- GitHub Copilot can't read zsh terminal output properly. Switching it to bash seems to be more reliable.\n- GitHub Copilot can't auto run commands, so have to repeatedly click to allow it to grep logs, etc. It looks like they might be fixing this soon.\n- GitHub Copilot seems to hardly ever consider the `copilot-instructions.md` file.\n- The \"lower\" models (GPT \u003c5, the free models with copilot, gemini) are often lazy and like to either propose changes vaguely, and not actually implement them even though they are in agent mode. Or they don't consider the full context, often deleting large swathes of code with placeholders like `# Rest of code here`. Sometimes I catch this, but I'm mostly not reviewing the code. Instead, commit often and revert if it seems to have regressed. I'm sure the spaghetti factor here is horrendous as a result.\n- The lower models often like to duplicate functions. Again it seems that they are not reviewing/considering the full context of the codebase (even when asked). Often times I've had to tell it to go and consolidate duplicate/similar methods. Similarly they have a tendency to create placeholder or stub methods.\n- As a result, using non-premium models is basically pointless / will result in a mess and set you back.\n\nA lot of these issues are fixed now (as of Feb '26).\n\n## Prerequisites\n\n- **Python 3.13+**\n- **pipenv** — install with `pip install pipenv` if you don't have it\n\nThe Pipfile pulls in [PySDL2](https://pypi.org/project/PySDL2/) (plus the bundled SDL2 binary), [Pillow](https://pypi.org/project/Pillow/) for screenshots, [Cython](https://cython.org/) for the accelerated PPU/APU/CPU, and a few build utilities.\n\n## Installation\n\n```bash\npipenv install\n```\n\nThis creates a virtualenv and installs all runtime dependencies.\n\n### Building the Cython extensions (required)\n\nThe PPU, APU, and CPU are implemented as Cython (`.pyx`) modules and **must be compiled** before running the emulator.\n\n```bash\npipenv run python setup.py build_ext --inplace\n```\n\nThis compiles `ppu.pyx`, `apu.pyx`, and `cpu.pyx` into native C extensions (`.so` on macOS/Linux, `.pyd` on Windows). You only need to rebuild after modifying a `.pyx` file.\n\n## Usage\n\nThe easiest way to run the emulator is via `run.sh`, which automatically builds the Cython extensions before launching:\n\n```bash\n./run.sh \u003crom_file\u003e\n```\n\nFor example:\n\n```bash\n./run.sh mario.nes\n```\n\nOr run manually after building:\n\n```bash\npipenv run python main.py mario.nes\n```\n\n### Headless Mode\n\nRun without opening an SDL window. The emulator runs for a set duration, prints periodic stats, and saves a final screenshot:\n\n```bash\npipenv run python main.py mario.nes --headless\npipenv run python main.py mario.nes --headless --duration 120 --screenshot output.png\n```\n\n- `--headless` — Run without a window (no SDL). Prints progress and saves a screenshot on exit.\n- `--duration \u003cseconds\u003e` — How many seconds to run in headless mode (default: `60`).\n- `--screenshot \u003cpath\u003e` — File path for the headless-mode screenshot (default: `headless_screenshot.png`).\n\n### Debug Headless Runner\n\nA separate script (`headless_run.py`) is available for debug-oriented headless runs with frame-level control and PPU log filtering:\n\n```bash\npipenv run python headless_run.py mario.nes --frames 120 --out full.log --hits spr0.log\n```\n\n- `--frames \u003cn\u003e` — Number of frames to run (default: `90`).\n- `--out \u003cpath\u003e` — Path to write full debug log output.\n- `--hits \u003cpath\u003e` — Path to write filtered sprite-0 hit lines.\n- `--continue-after-hit` — Don't stop early when a sprite-0 hit occurs; run the full frame count.\n\n### Controls\n\n**Player 1**\n\n- Arrow Keys — D-Pad\n- J — A Button\n- K — B Button\n- Right Shift — Select\n- Enter — Start\n\n**Player 2**\n\n- W/A/S/D — D-Pad\n- G — A Button\n- H — B Button\n- Tab — Select\n- Space — Start\n\n**General**\n\n- R — Reset System\n- F12 — Take Screenshot\n- Escape — Quit\n\n## Contributions\n\nContributions are welcome! Feel free to throw up a PR if you know of any changes that could help. Include screenshots before and after if fixing a graphics issue.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frichstokes%2Fnez","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frichstokes%2Fnez","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frichstokes%2Fnez/lists"}