Open-source project
binji/smolnes avatar
binji/smolnes

smolnes: a NES emulator in about 5000 bytes of C

NES emulator in <5000 bytes of C

796 stars48 forksCMIT

At a glance

What is it?
smolnes is a Nintendo Entertainment System emulator written in roughly 5000 significant bytes of C, small enough to read as a curiosity and to port as a demonstration. It runs mapper 0, 1, 2, 3, 4 and 7 games, and the repository ships both the obfuscated original and a readable version.
Who is it for?
smolnes earns a place in a teaching collection, a code golfing exercise or as a compact test case for emulator work, because it does in a few thousand bytes what most emulator projects spread over tens of thousands, and because the deobfuscated.c twin lets you read what the compact version does. Do not pick it as the emulator for playing a wide library, since mapper support stops at 0, 1, 2, 3, 4 and 7 and the README does not list mappers 5 or 6.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 19 days ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Five thousand bytes that boot a cartridge

smolnes is described as a NES emulator in about 5000 significant bytes of C. The phrase significant bytes is doing work there, and the README also says zero in the sense that the source is deliberately hard to read: the repository topics include obfuscated, and the source file smolnes.c is the compact one.

What the project demonstrates is that a working Nintendo Entertainment System emulator does not need to be large. The feature list is one line: it plays some mapper 0, 1, 2, 3, 4 and 7 games. That is a real constraint, not a promise of full coverage, and it is worth understanding before anything else.

The screenshots in the README show the scope in practice. There is Batman, Kirby's Adventure, Mega Man 3, Little Nemo the Dream Master, River City Ransom, Super Mario Bros. 2 and 3, Teenage Mutant Ninja Turtles 2, Bionic Commando, Donkey Kong, Dragon Warrior, Adventures of Lolo, The Legend of Zelda, Metroid, Contra, Castlevania, DuckTales, Battletoads and Gradius among others. Those titles cover a spread of mappers, since a mapper is the hardware mechanism a cartridge uses to map ROM and RAM to the console address space, and each one implies a different mapping scheme the emulator has to implement.

The audience is a specific kind of reader: people who study emulator internals, people who like code golf, and people who want a small enough NES emulator to read end to end.

Building it: make, gcc or clang, and SDL2 from pkg-config

The build section is brief and hedged. The README says the project probably only builds on Linux and macOS, and suggests trying gcc or clang.

bash
$ make

The Makefile at the root of the repository defines two targets, and they exist for a reason.

makefile
.PHONY: all clean
all: smolnes deobfuscated

The smolnes target compiles smolnes.c and the deobfuscated target compiles deobfuscated.c, both with the same flags: optimisation enabled, debug symbols included, and a long list of warning suppressions. The library flags come from pkg-config, so the SDL2 development files have to be installed for the build to find them.

makefile
SDLFLAGS=$(shell pkg-config --cflags --libs sdl2)

The suppression list is itself a clue about the source. The Makefile disables warnings for parentheses, misleading indentation, bool operation, discarded qualifiers, incompatible pointer types, unknown warning options, switch outside range, unused value, char subscripts and switch. A warning list that long is what you need when the code is written to be compact and hard to read rather than to be maintained.

There is also a CMakeLists.txt in the repository, so the build is not tied to make alone. The SDL directory is present with a .gitmodules file, so the SDL sources are pulled in as a submodule rather than vendored.

Running it: one ROM path on the command line

The emulator takes a single argument, the path to a ROM file.

bash
$ ./smolnes <rom.nes>

The argument is a placeholder in the README rather than a sample filename, and the repository contains no ROM files, so you supply your own image. The extension in the placeholder is the conventional one for a cartridge dump.

The key bindings are given as a table mapping NES controller buttons to keyboard keys: the dpad to the arrow keys, the B button to Z, the A button to X, Start to Enter and Select to Tab.

What the README does not describe is any of the surrounding behaviour. There is no mention of a pause control, no frame timing options, no configuration file, no way to change the window size, no sound options and no command line flags beyond the ROM path. For a project whose whole premise is minimal size, that restraint is consistent, but it does mean the experience is fixed once it runs.

It is also worth being clear about one boundary: the repository ships an emulator, not a library of games. Where the images come from is a separate question from whether the emulator works, and the README does not address it at all.

Changing the keys means editing line 20 of the source

The key configuration is not in a settings file, which is unusual enough to describe precisely.

The README tells you to look for line 20 in the source code. There is a table showing which characters map to which numbers, which default key they carry and which NES button they correspond to: the escape character maps to the number 27 and the X key for the A button, the hash character to 29 and Z for the B button, plus to 43 for Tab and Select, the open parenthesis to 40 for Return and Start, and the letters R, Q, P and O for the four directions.

The procedure that follows is three steps: find the keys you want to replace in the SDL scancode list, convert the number to its ASCII character or C character literal, and replace that character in the string.

That entire configuration surface is one line of source. Two consequences follow. Changing the controls means recompiling, and any change to the source shifts the line numbering, so the instruction to look at line 20 becomes approximate the moment you edit the file. The alternative approach for a project that wants a different layout would be to parse configuration at startup, which the size budget does not allow.

It is a small thing, and it is the clearest illustration of the trade the project makes throughout: every convenience that would cost bytes is left out.

smolnes.c and deobfuscated.c: the same emulator, twice

The repository contains both smolnes.c and deobfuscated.c, and the Makefile builds both. That pairing is the most useful thing in the project for anyone who wants to learn from it.

The obfuscated file is the compact one, and the deobfuscated file is the same program written out so a human can follow it. Reading the pair side by side shows what a code golfer actually gives up in order to hit a byte target: variable names collapse, control flow turns into goto style jumps, and structures that would normally be separate functions get inlined and reused.

There is also a template.txt file at the top level of the repository, and a screenshot of the source code in the README image directory. The template file is consistent with a workflow in which a readable version is expanded or compressed mechanically, rather than one in which two independent implementations are maintained by hand.

For a learner this is a better artefact than most emulator code, because the transformation is visible. For a user who just wants to play something, it makes no difference, and for someone planning to extend the emulator it raises the question of which file you would edit and how you would keep the pair in sync, which the README does not address.

What other projects do with it

The README keeps a list of projects that use smolnes, and it is more revealing than the feature list.

One is a port to Squirrel for Portal 2, which turns the emulator into something you can run inside a game. Another is a project that plays NES ROMs with the Ghidra PCode Emulator, which uses smolnes as a target program for a decompiler oriented research tool. A third is rv32emu, a compact RISC-V RV32I[MAFC] emulator, which uses smolnes as a demo. A fourth is a Haxe rewrite, hxNes, built with HaxeFlixel, which has sound and includes a debugging mode feature.

There is also a link to a piece of vulnerability research on smolnes.

Two things fall out of this list. First, smolnes is used less as a product and more as a compact, self contained target: small enough to embed in another emulator, to run inside a game, or to be picked apart by a research tool. Second, the Haxe rewrite is the real alternative to consider, and the difference in approach is explicit in the README's own description: hxNes has sound and a debugging mode, which smolnes does not have, because smolnes spends its bytes on the emulator core rather than on the interface.

So the choice is between a few thousand bytes that cover a few mappers and nothing else, and a larger rewrite that trades size for features.

Licence, activity and what is not documented

smolnes is MIT licensed, with the LICENSE file in the repository root, which is an unusually permissive and uncomplicated position for a project people port and rewrite.

The repository is not archived, and the last push was on 2026-10-04, so the work is current. There are no GitHub releases, so there is no version to pin and no changelog, which for a project people embed in other software is the main practical gap.

What the README leaves undocumented is a short list worth knowing. There is no description of which CPU and PPU behaviours are implemented, no account of the mapper support beyond the list of numbers, no statement about the audio path, and no mention of save states, rewinding or any test suite. There is also no explanation of the deobfuscation workflow, so the relationship between smolnes.c, deobfuscated.c and template.txt is inferred from the file names and the build targets rather than documented.

The build guidance is similarly hedged: probably only builds on Linux and macOS, try gcc or clang. That is an honest warning rather than a support policy, and it means a Windows user is left with the CMakeLists.txt and their own SDL2 setup.

For a project of this size those gaps are understandable. The trade is that you get something small enough to hold in your head and little else, and the README's own framing, a list of games it plays and a list of projects that borrow it, is the honest summary of what it is for.

Editorial conclusion

smolnes earns a place in a teaching collection, a code golfing exercise or as a compact test case for emulator work, because it does in a few thousand bytes what most emulator projects spread over tens of thousands, and because the deobfuscated.c twin lets you read what the compact version does. Do not pick it as the emulator for playing a wide library, since mapper support stops at 0, 1, 2, 3, 4 and 7 and the README does not list mappers 5 or 6. Before you build, confirm you have SDL2 development files and a C compiler on Linux or macOS, since the README says it probably only builds there, and read deobfuscated.c rather than smolnes.c first if you intend to study the code.

Frequently asked questions

How big is the smolnes NES emulator and what does it support?

The README describes it as a NES emulator in about 5000 significant bytes of C, and the feature list says it plays some mapper 0, 1, 2, 3, 4 and 7 games. The repository contains no ROM files, so you supply your own cartridge image.

How do I build smolnes?

Run make in the repository root, which builds the smolnes and deobfuscated binaries using the compiler in $CC with SDL2 flags from pkg-config. The README says the project probably only builds on Linux and macOS and suggests gcc or clang.

Which keyboard keys control smolnes?

The arrow keys map to the dpad, Z is the B button, X is the A button, Enter is Start and Tab is Select. To change them you edit the character string at line 20 of the source, using the SDL scancode numbers listed in the README.

What is the difference between smolnes.c and deobfuscated.c?

Both are built by the Makefile. smolnes.c is the compact, obfuscated source, while deobfuscated.c is the same emulator written out so it can be read, and the repository also carries a template.txt used for that workflow.

Can smolnes play every NES game?

No. Mapper support is limited to mappers 0, 1, 2, 3, 4 and 7, and the README only claims that it plays some games using those mappers. The README does not list mapper 5 or 6 support.

Official sources

  1. binji/smolnes on GitHub
  2. Issues
  3. License: MIT
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/binji-smolnes.svg)](https://hysenlabs.com/projects/binji-smolnes)

Was this useful?