Browse by type
Anemoia-ESP32
Anemoia-ESP32 is a rewrite and port of the Anemoia Nintendo Entertainment System (NES) emulator running directly on the ESP32. It is written in C++ and is designed to bring classic NES games to the ESP32 with support for both TFT displays and composite video output. This project focuses on performance, being able to run the emulator at native speeds and with full audio emulation and save states implemented.
Flash the emulator instantly using the Web Flash!
Anemoia-ESP32 is available on GitHub under the GNU General Public License v3.0 (GPLv3).
This project is proudly sponsored by NextPCB. Their support helps fund the development and continuation of this project, and I'm very grateful to have them as my first ever sponsor.
Want to make a PCB? NextPCB offers PCB fabrication and assembly services with fast turnaround times and affordable pricing to help bring your electronics projects to the next level.
Anemoia-ESP32 is heavily optimized to achieve native NES speeds on the ESP32, running at ~60.098 FPS (NTSC) with 1 frame skip and full audio emulation enabled.
Here are the performance benchmarks for several popular NES games.
[!NOTE] The following benchmarks show average framerates recorded over 8192 frames (~2 minutes) of emulation time. Some games, such as
Kirby's Adventure, which frequently switch banks may experience significant FPS drops in certain sections.
| Game | Mapper | Average FPS |
|---|---|---|
| Super Mario Bros. | NROM (0) | 60.10 FPS |
| Contra | UxROM (2) | 60.10 FPS |
| The Legend of Zelda | MMC1 (1) | 60.10 FPS |
| Mega Man 2 | MMC1 (1) | 60.10 FPS |
| Castlevania | UxROM (2) | 60.10 FPS |
| Metroid | MMC1 (1) | 60.10 FPS |
| Kirby's Adventure | MMC3 (4) | 59.57 FPS |
| Donkey Kong | NROM (0) | 60.10 FPS |
As of now, Anemoia-ESP32 has implemented six major memory mappers: * Mapper 0 * Mapper 1 * Mapper 2 * Mapper 3 * Mapper 4 * Mapper 69
Totalling to around 79% of the entire NES game catalogue.
If you'd like to check if a certain game is supported, visit
NesCartDB and search for the game on the
right-hand side of the site. Select the specific game version
and look for the iNES Mapper number in the cart properties.
The game should be supported if the iNES Mapper number is in the list
of implemented mappers above.
Feel free to open an issue if a game has glitches or fails to boot.
Anemoia-ESP32 requires a dual-core ESP32 with a minimum of 1 MB flash memory and NO PSRAM IS REQUIRED.
Anemoia-ESP32 supports composite video output via the COMPOSITE_VIDEO define in config.h, based on esp_8_bit by Peter Barrett. This lets the emulator output directly to a CRT television or any display with a composite input.
Additional hardware needed: - Any CRT or display with a composite RCA input - 1kΩ resistor and 10nF capacitor
Wiring:
-----------
| |
| 25 |-------------------------------► Video out (RCA)
| |
| 18 |----[1kΩ]----+---------------► Audio out
| ESP32 | |
| | ---
| | --- 10nF
| | |
| | ▼ GND
-----------
| Signal | ESP32 Pin |
|---|---|
| Video out | GPIO25 |
| Audio out | GPIO18 |
[!NOTE] GPIO18 is the default audio pin and can be changed via
AUDIO_PINinconfig.h.
To enable composite video, open config.h and uncomment:
#define COMPOSITE_VIDEO
Then set your video standard and audio pin as needed:
#define VIDEO_STANDARD 1 // 0 = PAL, 1 = NTSC
#define AUDIO_PIN 18
[!IMPORTANT] Composite video and TFT output are mutually exclusive. Enabling
COMPOSITE_VIDEOdisables the SPI display pipeline entirely.
[!NOTE] ST7789-based displays are recommended as they seem to fare better with 80MHz SPI speeds and are the most compatible.
[!IMPORTANT] ILI9341 users: ILI9341-based screens may experience display problems at 80MHz. Reduce the SPI frequency to 40MHz in your
User_Setup.h. This will cause the emulator to run a few FPS slower than ST7789 screens.
| Signal | ESP32 Pins |
|---|---|
| MOSI | GPIO23 |
| MISO | -1 (N/A) |
| SCLK | GPIO18 |
| CS | GPIO5 |
| DC | GPIO2 |
| RST | EN |
| Signal | ESP32 Pins |
|---|---|
| MOSI | GPIO13 |
| MISO | GPIO12 |
| SCLK | GPIO14 |
| CS | GND |
[!IMPORTANT]
If using this 3.3V microSD card module, the pull-up resistor on MISO (GPIO12) must be removed. GPIO12 is a bootstrapping pin (MTDI) that must be LOW during boot. The external pull-up on the microSD module conflicts with the boot strapping process, preventing the ESP32 from booting correctly.
| Signal | ESP32 Pins |
|---|---|
| Input | GPIO25 |
There are currently four input methods: Tactile push buttons, an NES/SNES controller, a PS1/PS2 controller, and a Serial controller.
| Signal | ESP32 Pins |
|---|---|
| A | GPIO19 & GND |
| B | GPIO26 & GND |
| Left | GPIO32 & GND |
| Right | GPIO33 & GND |
| Up | GPIO15 & GND |
| Down | GPIO4 & GND |
| Start | GPIO27 & GND |
| Select | GPIO16 (RX2) & GND |
| Signal | ESP32 Pins |
|---|---|
| Clock | GPIO32 |
| Latch | GPIO33 |
| Data | GPIO35 |
| Signal | ESP32 Pins |
|---|---|
| Data | GPIO32 |
| Command | GPIO33 |
| Attention | GPIO26 |
| Clock | GPIO27 |
Also connect the power and ground lines if using a controller. Most controllers should work fine from 3.3V power supply.
Button presses can be sent over serial via two independent methods, provided by the SerialGameControllerAdapter project. Both can coexist and are handled separately.
Button input is read over the main USB serial connection. Open WebSerialController.html in a Chromium-based browser — it translates keyboard, mouse, touch, or USB controller input into serial button commands. No extra hardware is required, making it ideal for testing Anemoia-ESP32 before any wiring or soldering.
A second ESP32 running SerialGameControllerAdapter firmware. The adapter reads inputs from an NES, SNES, PS1, PS2, or Bluetooth controller and forwards button presses over a secondary serial port (Serial1). A separate UART port is used specifically to avoid interfering with USB programming of the main board.
| Signal | CYD Pin |
|---|---|
| TX (Adapter) → RX (ESP32) | GPIO22 |
| RX (Adapter) ← TX (ESP32) | GPIO27 |
Cheap Yellow Displays (CYD) are an all-in-one ESP32 board that comes with most of the hardware needed in this project already integrated, making it ideal for Anemoia-ESP32. Because of the limited pins brought out by the CYD, it is only practical to use a NES controller or a serial controller.
Hardware Needed: - Cheap Yellow Display - NES/SNES controller (or serial controller — see Serial Controller) - Speaker (optional) - Can be attached with a 1.25mm JST connector to "SPEAK" or soldered directly
| Signal | ESP32 Pins |
|---|---|
| Clock | GPIO22 (CN1/P3) |
| Latch | GPIO27 (CN1) |
| Data | GPIO35 (P3) |
The schematics, PCB design files, enclosures, and 3D models are available in the /hardware and /3d-model folder.
A PCB that provides a clean, organized way to connect and manage all peripheral modules in one place.

A PCB that offers a more complete, permanent, and compact handheld by using discrete ICs instead of breakout modules.

These are the recommended parts to use for this project.
These are affiliate links. Buying through them helps support me at no extra cost to you. Thank you for your support.
Press Start + Select simultaneously in a game to open the menu. Press Select to change the ROM backend. See ROM Storage Backends for details.
| NES Button | SNES Buttons |
|---|---|
| A | B, A, R |
| B | Y, X, L |
| Start | Start |
| Select | Select |
| Up | D-Pad Up |
| Down | D-Pad Down |
| Left | D-Pad Left |
| Right | D-Pad Right |
| NES Button | PS1/PS2 Buttons |
|---|---|
| A | R1, R2, R3, X, O |
| B | L1, L2, L3, Square, Triangle |
| Start | Start |
| Select | Select |
| Up | D-Pad Up |
| Down | D-Pad Down |
| Left | D-Pad Left |
| Right | D-Pad Right |
ROMs are always sourced from the SD card.
$ claude mcp add Anemoia-ESP32 \
-- python -m otcore.mcp_server <graph>