Library / SDK
deltabeard/Peanut-GB avatar
deltabeard/Peanut-GB

Peanut-GB: a single-header Game Boy emulator fast enough for a microcontroller

A very fast and portable Game Boy (DMG) emulator single header library written in C99.

537 stars73 forksCLicense varies

At a glance

What is it?
Peanut-GB is a C99 Game Boy emulator that lives in one header file, peanut_gb.h, with no allocation, no threads and no dependencies, so it can run full speed on an RP2040 microcontroller. The trade is accuracy: the README states plainly that some games may run incorrectly or not at all.
Who is it for?
Adopt Peanut-GB if you are building an emulator on a microcontroller, a handheld or a custom device and need a DMG core that fits in a single header with no dependencies. Do not adopt it if cycle-accurate emulation matters, because the README asks you to seek an alternative emulator if accuracy is important.
Can I use it commercially?
Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
Is it still maintained?
Yes. The repository last received commits 32 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

One header file, and what it deliberately leaves out

Peanut-GB is a single file header Game Boy emulator library based off of gregtour's gameboy emulator, and the aim is stated as a high speed and portable Game Boy (DMG) emulator library that may be used for any platform that has a C99 compiler.

The shape of the repository says most of it. The root contains peanut_gb.h and version.all, plus a test/ directory and the examples/ folder. There is no src/ directory, no build system for the library itself and no dependency manifest. The emulator is one header file that you include.

That constraint is the design. There is no heap allocation, no operating system, no threading inside the core and no graphics or audio library baked in. What the core provides is the CPU, the memory map, the cartridge mappers, the LCD timing and the timers. Everything that touches the outside world is a callback you provide.

The performance claim is the reason the project gets attention. The emulator is very fast, so much so that it can run at full speed on the Raspberry Pi Pico, and the features list repeats it: fast enough to run on a RP2040 ARM Cortex M0+ microcontroller at full speed. A Cortex M0+ is the cheapest, most constrained core in the RP2040, so this is not a claim about a fast machine running a small workload.

The scope is narrow in one other direction. Only the original Game Boy (DMG) is supported at this time, though preliminary work has been completed to support Game Boy Color, with the tracking issue in the repository. And the project describes itself as a work in progress that can be inaccurate, although it does pass Blargg's CPU instructions and instruction timing tests. As such, some games may run incorrectly or not run at all, and the README asks you to seek an alternative emulator if accuracy is important.

That last sentence is the honest framing of the whole project, and it is the right one to hold on to. The primary language is C, the default branch is master, and the recent releases are v1.3.0 from 2025-06-11, v1.2.0 from 2023-05-06 and v1.1.0 from 2022-05-04. The last push was on 2026-09-21, and the licence is not stated in the repository metadata, which is something to settle before you ship anything built on it.

What the core covers and what your front end must supply

The integration contract is short and explicit, and it is the part you implement when you build a front end.

There are required functions. The front-end implementation must provide a number of functions to the library, and these functions are set when calling gb_init. They are gb_rom_read, gb_cart_ram_read, gb_cart_ram_write and gb_error.

Read that list again and you can see the whole boundary of the emulator core. The library reads ROM data through your callback, reads and writes cartridge RAM through your callbacks, and reports errors through yours. It never touches the filesystem, never allocates and never calls into an operating system. The library is the machine; your code is the machine's world.

Then there are optional functions, which enable subsystems.

For the LCD, the function is lcd_draw_line, and it is required for LCD drawing. You set it using gb_init_lcd and enable LCD functionality within Peanut-GB by defining ENABLE_LCD to 1 before including peanut_gb.h. ENABLE_LCD is set to 1 by default if it was not previously defined, and if gb_init_lcd is not called or lcd_draw_line is set to NULL, then LCD drawing is disabled.

The pixel data sent to lcd_draw_line comes with both shade and layer data. The first two least significant bits are the shade data, black, dark, light and white. Bits 4 and 5 are layer data, OBJ0, OBJ1 and BG, which can be used to add more colours to the game in the same way that the Game Boy Color does to older Game Boy games.

That layer mechanism is the interesting bit. The DMG hardware has four shades per palette, but the PPU still tracks which of the background or the two sprite layers a pixel belongs to. Peanut-GB passes that layer information up to you, and a front end with a richer palette, as the features list puts it, allows different palettes on background and sprites. On a microcontroller with an RGB display, that is the difference between four colours and something closer to what the hardware was doing.

For sound, the functions are audio_read and audio_write, required for audio emulation and output. Peanut-GB does not include audio emulation, so an external library must be used, and audio output must be enabled by defining ENABLE_SOUND to 1 before including peanut_gb.h.

That is a significant architectural point. The CPU is emulated, the PPU is emulated, the timers are emulated, but the APU is not, and the README says so plainly. If sound is enabled, an external audio processing unit (APU) library is required, and a fast one is included in the repository under the SDL2 example at examples/sdl2/minigb_apu.

For serial communication, the functions are gb_serial_tx and gb_serial_rx, set using gb_init_serial. If these functions are not set, then the emulation will act as though no link cable is connected.

Documentation of the function prototypes can be found at the bottom of peanut_gb.h, which is a slightly unusual place for documentation but a natural one for a single-header library.

Hardware support and the accuracy caveats

The feature list is short and hardware-focused.

Game Boy (DMG) support, very fast, MBC1, MBC2, MBC3 and MBC5 support, Real Time Clock support, serial connection support, and the ability to be used with or without a bootrom. That last one matters more than it sounds: many emulators need the original bootrom because the DMG boot sequence does things that are hard to reproduce, and a core that runs without one is easier to embed and easier to test.

The rest of the list is about presentation and flexibility. Different palettes on background and sprites, frame skip and interlacing modes which the README says are useful for slow LCDs, and the fact that LCD and sound can be disabled at compile time.

That last point is worth connecting to the architecture. On a microcontroller with a small display and no audio hardware, ENABLE_LCD and ENABLE_SOUND being compile-time switches means you are not paying for the code paths you do not use. The frame skip and interlace modes exist for the same reason: a slow LCD cannot keep up with the emulator's native rate, and a controller that can drop frames or interlace the output is what makes the emulator usable on that hardware rather than merely fast.

The caveats section is where the honesty is. The LCD rendering is performed line by line, so certain animations will not render properly, with Prehistorik Man named as an example. Some games may not be playable due to emulation inaccuracy, with an issue in the repository tracking that. And the MiniGB APU runs in a separate thread, so the timing is not accurate; if accurate APU timing and emulation is required, then Blargg's Gb_Snd_Emu library, or an alternative, can be used instead.

That last caveat is a good example of a design trade made explicitly. The bundled APU is fast and simple, running in its own thread so the emulator does not stall on audio, and it accepts that its timing is wrong. For a game that cares about sound timing this is unacceptable, and the README names the library to swap in. That is a better situation than an emulator that claims accuracy it does not have.

The SDL2 example and the flags that configure the core

The flagship example implementation is given in peanut_sdl.c, which uses SDL2 to draw the screen and take input. Run cmake or make in the ./examples/sdl2/ folder to compile it.

Running it gives you a drop-zone window that you can drag and drop a ROM file into. Alternatively, run in a terminal using peanut-sdl game.gb, which will automatically create the save file game.sav for the game if one isn't found. Or run with peanut-sdl game.gb save.sav to specify a save file.

The examples directory shows the range of front ends the core supports. There is the SDL2 example, a mini_fb directory which is the framebuffer front end, a debug/ directory, a debug-nuklear/ directory, and a benchmark/ directory.

The benchmark directory is worth noting. A library whose selling point is speed needs a way to measure speed, and shipping a benchmark alongside the examples rather than only a test suite is what makes the performance claim checkable by the people using it.

The compile-time flags are what let you strip the core down for a target device, and they are the configuration surface you will actually tune.

bash
cd ./examples/sdl2/
cmake

Both are documented in the README in the context of the optional functions. ENABLE_LCD must be 1 before including peanut_gb.h for LCD drawing to be available, and is defaulted to 1 if it was not previously defined. ENABLE_SOUND must be 1 for audio output, and it requires the external APU library to be linked in.

The control mapping in the SDL2 example is a reasonable default and gives a sense of what a front end has to handle.

code
| Action            | Keyboard   | Joypad |
|-------------------|------------|--------|
| A                 | z          | A      |
| B                 | x          | B      |
| Start             | Return     | START  |
| Select            | Backspace  | BACK   |
| D-Pad             | Arrow Keys | DPAD   |

Frameskip and Interlaced modes are both off by default, and the Frameskip toggles between 60 FPS and 30 FPS. The turbo keys, x2 hold on Space and x2, x3, x4 as toggles on 2, 3 and 4, are a front-end convenience rather than something the core provides, which tells you where the line between emulation and presentation sits.

Who is using it, and what that tells you about the core

The README lists projects using Peanut-GB, described as a non-exhaustive list in no particular order, and the list is more informative than a typical credits section.

Pico-GB is Game Boy emulation on the Raspberry Pi RP2040 microcontroller, and Peanut_gb-RGFW is an example for RGFW, a windowing library for the same class of hardware. CPBoy is a Game Boy emulator for the Classpad II calculator, the fx-CP400. PlayGB is a Game Boy emulator for Playdate, the little handheld with a crank.

Then the microcontroller ones get more specific. AcolyteHandPICd32 is Game Boy emulation on the PIC32MZ2048EFH144, a 32-bit 150MHz microcontroller. PicoPeanutGB is a Game Boy and Game Boy Color emulator for RP2350-based microcontrollers. retroJam is a multi retro console emulator for the Adafruit Fruit Jam. And 3DGB is a 3D-rendered Game Boy emulator for the Nintendo 3DS that uses Peanut-GB as its emulation core.

Three things fall out of that list.

The first is that the single-header design is what makes those ports possible. A calculator, a handheld with a crank and a 3DS homebrew are all environments where pulling in a build system and a dependency tree is a problem, and a header you include is not.

The second is the range of host hardware, from a 150MHz PIC32 to a dual-core ARM Cortex M0+ to a 3DS. The claim about running at full speed on the Cortex M0+ is the demanding end of that range, and the fact that a PIC32 and an M0+ both work suggests the core is written with few assumptions about the host.

The third is that 3DGB uses it as an emulation core while rendering in 3D, which is the clearest demonstration that the library is a core rather than a program. The CPU, memory map and PPU semantics are reused while the entire presentation layer is replaced, which is only possible because the boundary between the two is the callback interface described earlier.

The homepage is at projects.deltabeard.com/peanutgb/, the repository carries a .astylerc and an .editorconfig, and there is a CONTRIBUTING.md alongside the test/ directory.

Where Peanut-GB is the wrong choice

There are three limits, and the first one is the one the README puts in bold on your behalf.

The first is accuracy. The emulator is a work in progress and can be inaccurate, some games may run incorrectly or not run at all, and if accuracy is important you should seek an alternative. The line-by-line LCD rendering breaks specific animations, the bundled APU's timing is wrong because it runs in a separate thread, and the game-specific issues are tracked in an open issue. That is a coherent position for a library whose goal is speed and small footprint, but it means this is not the core to build a speedrun on, a TAS on, or a compatibility-test suite with.

The second is the DMG-only scope. Only the original Game Boy is supported, with Game Boy Color work described as preliminary. Projects like PicoPeanutGB and 3DGB are described as Game Boy and Game Boy Color emulators, which suggests the GBC work has moved further than the README's wording suggests, but the documented state of the core is DMG with a preliminary GBC path.

The third is the licensing question, and it is unresolved in what the repository states. The licence field on the repository is unknown, and there is no LICENSE file in the top-level entries. The LICENSE file is not among the repository's entries, unlike the LICENSE.MD in some of the other projects in this space. That is a real gap for anyone planning to ship a device built on the core, and it is a gap you have to close by asking rather than by reading.

The honest alternatives split by what you need. For accuracy, the reference implementations with cycle-accurate CPU, PPU and APU, tested against Blargg's test ROMs, are the ones to use, and the README itself points at Blargg's Gb_Snd_Emu for accurate sound timing. The difference in approach is that those emulators model the hardware precisely and pay for it in speed, while Peanut-GB models it well enough to be fast and small and accepts the gaps it documents.

For a device with more resources, the choice is between embedding a more accurate core and running a full emulator. If your target has a display, audio and storage, running an established emulator and skipping the integration work entirely is often simpler than writing a front end, and you get compatibility for free.

What Peanut-GB gives you that those do not is a core small enough to fit on a microcontroller with no operating system and no allocation. If that is your constraint, the accuracy caveats are the price and the README tells you what you are paying.

Editorial conclusion

Adopt Peanut-GB if you are building an emulator on a microcontroller, a handheld or a custom device and need a DMG core that fits in a single header with no dependencies. Do not adopt it if cycle-accurate emulation matters, because the README asks you to seek an alternative emulator if accuracy is important. Before you commit, run the Blargg CPU instruction and timing tests against your build, and note the last push was on 2026-09-21 with release v1.3.0 from 2025-06-11.

Frequently asked questions

What is Peanut-GB and how does it integrate with a front end?

It is a single file header Game Boy emulator library in C99, with the whole emulator in peanut_gb.h. The front end must provide gb_rom_read, gb_cart_ram_read, gb_cart_ram_write and gb_error, set when calling gb_init, and may provide lcd_draw_line, audio_read, audio_write and the serial callbacks to enable those subsystems.

Does Peanut-GB emulate Game Boy Color?

Only the original Game Boy (DMG) is supported at this time, though preliminary work has been completed to support Game Boy Color, tracked in issue 50 in the repository. There are downstream projects described as Game Boy and Game Boy Color emulators built on the core.

How do I enable the LCD and sound in Peanut-GB?

Define ENABLE_LCD to 1 before including peanut_gb.h for LCD drawing, and set it through gb_init_lcd with the lcd_draw_line callback. For sound, define ENABLE_SOUND to 1 and link an external APU library, since Peanut-GB does not include audio emulation. The README notes ENABLE_LCD defaults to 1 if it was not previously defined.

How accurate is Peanut-GB, and what does the README say to do?

It is described as a work in progress that can be inaccurate, although it passes Blargg's CPU instructions and instruction timing tests, so some games may run incorrectly or not run at all. The README says to seek an alternative emulator if accuracy is important. The LCD renders line by line, which breaks some animations, and the bundled MiniGB APU runs in a separate thread so its timing is not accurate.

How do I build and run the SDL2 example of Peanut-GB?

The flagship example implementation is peanut_sdl.c, which uses SDL2 to draw the screen and take input, and you compile it by running cmake or make in the ./examples/sdl2/ folder. Running peanut-sdl creates a drop-zone window you can drag a ROM into, or you can run peanut-sdl game.gb in a terminal, which creates game.sav automatically if none is found.

Official sources

  1. deltabeard/Peanut-GB on GitHub
  2. Issues
  3. Project website
  4. README
  5. Releases
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/deltabeard-peanut-gb.svg)](https://hysenlabs.com/projects/deltabeard-peanut-gb)