Library / SDK
nathhB/nbnet avatar
nathhB/nbnet

nbnet: a single-header C library for game networking over UDP or WebRTC

single header C(99) library to implement client-server network code for games

566 stars33 forksCZlib

At a glance

What is it?
nbnet is a single-header C99 library that implements client-server networking for online games, with reliable and unreliable messages, fragmentation for payloads larger than the MTU, bit-level serialization, network condition simulation and pluggable UDP or WebRTC drivers. It suits a C developer building a multiplayer game or learning how netcode works, and the author says plainly that it is a learning project rather than a battle-tested one.
Who is it for?
nbnet fits a C developer who wants to see a working netcode implementation in one readable header, since the whole library is a single file with the design taken from Glenn Fiedler's article series, and the examples include a raylib integration, an RPC example and a browser-playable game.
Can I use it commercially?
Yes. Zlib 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 21 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, taken from a well-known article series

nbnet is described as a single header C, C99, library designed to implement client-server architecture, more precisely for online video games. The readme is explicit about where the design comes from: it is based on a series of articles by Glenn Fiedler on the topic, the articles on gafferongames.com that most modern game netcode is descended from.

That provenance is the single most useful fact about the project for an engineer evaluating it. The reliable-ordered, unreliable-ordered, unreliable-unsequenced message scheme, the idea of a packet containing a sequence number, an ack sequence number and an ack bitfield, and the split between a simulation layer and a transport layer are all ideas with a written explanation you can read alongside the code. A library that follows a published design is much easier to reason about than one that invents a private one, and for a library whose author describes it as a learning project, that is the point.

The repository layout matches the single-header claim. nbnet.h sits at the root as the entire implementation, net_drivers/ holds the transport drivers, examples/ contains the sample programs with their own CMakeLists.txt, and there are directories for tests, soak testing under soak/, docs, and bin/. There is a WEBRTC.md alongside the readme for the WebRTC path.

The features list covers connection management, sending and receiving both reliable ordered and unreliable ordered messages, sending messages larger than the MTU through a message fragmentation system, bit-level serialization of integers, floats, booleans and byte arrays for bandwidth, network condition simulation covering ping, jitter, packet loss, packet duplication and out-of-order packets, network statistics for ping, bandwidth in both directions and packet loss, and Web support both natively and through WebAssembly using emscripten.

The licence is Zlib, and the released versions are 2.0 from 2024-02-11, 1.1.3 from 2023-08-19 and 1.1.2 from 2023-07-11.

The drivers, and why the transport is not in the header

nbnet does not implement any low-level transport code. It relies on drivers, and the readme defines a driver precisely: a set of function definitions that live outside the nbnet header and provide a transport layer implementation for nbnet to send and receive packets.

Three ready-made drivers ship with the library. The UDP driver works with a single UDP socket and is designed for desktop games. The WebRTC driver in WebAssembly form works with a single unreliable and unordered data channel and is implemented in JavaScript using the emscripten API. The native WebRTC driver works the same way but can be compiled natively rather than to WebAssembly.

The design reason for separating them is straightforward. A socket, a data channel and a browser's WebRTC stack have nothing in common at the API level, but the game logic above them only needs to send a packet and receive a packet. Keeping the transport behind a function-pointer table means the netcode above it does not change when you move from a desktop UDP socket to a browser data channel, which is the whole point of a driver abstraction in a library that targets both.

The native WebRTC driver has external dependencies that the readme lists: libdatachannel for the WebRTC stack itself and json.h for JSON parsing.

The consequence worth noting is that WebRTC is not free. Where the UDP driver is a socket you already have, the WebRTC path drags in a media stack, a signalling story and a browser, and the data channel it uses is unreliable and unordered by construction. Getting reliable ordered delivery over it is the driver's job.

The UDP driver being described as designed for desktop games, and the WebRTC drivers being aimed at the web, tells you the intended split: native for native, browser for browser.

Integrating it, and the two things you have to supply

The integration is short because the header is one file, and the readme states the setup rules plainly.

In exactly one of your source files you define the implementation macro and include the header:

c
#define NBNET_IMPL

#include "nbnet.h"

Then you add the driver header, in the same file where you defined NBNET_IMPL. For the UDP driver that is:

c
#include "net_drivers/udp.h"

The first thing you have to supply is logging, because nbnet provides none. You define the log macros to point at whatever you already use:

c
#define NBN_LogInfo(...) SomeLoggingFunction(__VA_ARGS__)
#define NBN_LogError(...) SomeLoggingFunction(__VA_ARGS__)
#define NBN_LogDebug(...) SomeLoggingFunction(__VA_ARGS__)
#define NBN_LogTrace(...) SomeLoggingFunction(__VA_ARGS__)
#define NBN_LogWarning(...) SomeLoggingFunction(__VA_ARGS__)

The second is memory management. nbnet uses malloc, realloc and free, and you can redefine them:

c
#define NBN_Allocator malloc
#define NBN_Reallocator realloc
#define NBN_Deallocator free

That second point is more than a convenience. Redefining the allocator is how you route every allocation through an arena, a pool or a tracking allocator, which in a game engine is usually the difference between a network layer that participates in your memory budget and one that does not.

After that the readme says all set, from here it suggests you hop into the examples. The examples directory contains echo, echo_bytes, raylib and rpc, so there is a raylib integration for people writing games with that framework and a separate RPC example for the case where you want to call functions across the network rather than send messages.

The API design point that makes this work is event polling. The readme describes the API as straightforward and reliant on event polling, making it easy to integrate into a game loop. There is no thread, no callback queue and no hidden async runtime: the game loop asks nbnet what happened and acts on it. That is how you avoid taking the network thread's locks in your render code.

Bit-level serialization and the raw byte escape hatch

nbnet includes a primitive bit-level serialization system, and the readme is careful to describe it as primitive.

The bit-level part covers integers signed and unsigned, floats, booleans and byte arrays, and the stated purpose is bandwidth optimization. For a game sending position updates and input commands at high frequency, the difference between sending a float as four bytes and sending it as part of a bit-packed payload is the difference between a network bill and a network problem.

The word primitive matters though. This is a serializer for the handful of types a netcode needs, not a general-purpose serialization framework, and there is no schema, no versioning and no reflection. You write the packing yourself, by hand, and both peers have to agree on the layout.

That is exactly why the readme documents the alternative. If you want to use your own serialization solution, nbnet lets you send and receive raw byte arrays instead. The echo_bytes example exists to demonstrate that path, and the top-level description of the feature is that nbnet provides a primitive bit-level serialization system but lets you send and receive raw byte arrays if you would rather not use it.

So the choice is between two integration styles: hand-packed bits for bandwidth-critical traffic, or your own serializer over the byte array path when you have a schema already and would rather not maintain a parallel packed format. Both are supported, and the byte path is the one to prefer when you are integrating with an existing engine or with a codebase that already has a wire format.

It is worth noting that nbnet does not implement any low-level transport code, but it does implement the framing: the packet structure, the sequence numbers, the acks, the fragmentation and the reassembly. Those are the parts you would otherwise write yourself, and they are the parts that are easy to get subtly wrong.

Where nbnet is the wrong choice

The first limitation is stated by the author in the readme and should be the first thing in any evaluation. There is a disclaimer: nbnet is in the early stages of its development and is, first and foremost, a learning project of the author as they explore online game development. If you are looking for a battle-tested, production-ready library, this is probably not the one.

The second is the version and documentation situation, which the readme handles with an important note at the top. If you are interested in giving nbnet a try, use version 3.0, available on the 3.0 branch. The author says they are still working on it, that it is usable already and will be released soon, that 3.0 improves a lot internally and provides a cleaner API, and that the documentation is lacking because all the readmes are obsolete. The guidance is to look at the examples, which are up to date.

That is an unusual and honest situation. The recommended version is unreleased, the documentation for the released versions is stale, and the current documentation is the example code. The tagged releases stop at 2.0 from 2024-02-11 while the last push was on 2026-10-01, so the branch you would actually use is ahead of any tag.

The third is that WebRTC is a substantial dependency for what it delivers. The native driver needs libdatachannel and json.h, and the whole WebRTC path needs a signalling mechanism and a browser environment, which the readme covers in a separate WEBRTC.md rather than in the main documentation.

The fourth is that there is no encryption or authentication in the feature list. The features are connection management, message delivery, fragmentation, serialization, simulation and statistics. Nothing in that list is a transport-level security story, and for a networked game that is a decision you have to make deliberately rather than one the library makes for you.

The fifth is the ecosystem around it. The bindings list contains one entry, nbnet-sunder, contributed and explicitly not officially supported so it may be outdated, and the games made with nbnet are demos: a browser action game, a work in progress battle royale and a tank game prototype. That is exactly the right amount of evidence for a learning project, and exactly the wrong amount for a commercial launch.

Editorial conclusion

nbnet fits a C developer who wants to see a working netcode implementation in one readable header, since the whole library is a single file with the design taken from Glenn Fiedler's article series, and the examples include a raylib integration, an RPC example and a browser-playable game. It is the wrong choice for a shipped commercial multiplayer title, because the readme's own disclaimer calls it a learning project in its early stages, and it is the wrong choice if you need the stable 2.0 release with current documentation, since the 3.0 branch is recommended for new work while every readme is described as obsolete. Before adopting it, read the 3.0 examples rather than the readme, note that the latest tagged release is 2.0 from 2024-02-11, and plan for the event-polling integration into your own game loop.

Frequently asked questions

What is nbnet?

nbnet is a single header C, C99, library for implementing client-server network code for online games. It is based on Glenn Fiedler's series of articles on game netcode, and its API is built around event polling so it integrates into a game loop rather than running its own thread.

How do I use nbnet in a C project?

In exactly one source file define NBNET_IMPL and include nbnet.h, then include the driver header such as net_drivers/udp.h in that same file. You must also supply your own logging macros and, optionally, redefine the allocator macros to route allocation through your own allocator.

Which network transports does nbnet support?

Three drivers ship with it: UDP over a single socket for desktop games, WebRTC through WebAssembly using a single unreliable and unordered data channel implemented with the emscripten API, and a native WebRTC driver that works the same way but compiles natively. The native WebRTC driver relies on libdatachannel and json.h.

Which version of nbnet should I use?

The readme recommends version 3.0, available on the 3.0 branch, saying it improves things internally and gives a cleaner API, and warns that the documentation is lacking because all the readmes are obsolete and the examples are the up-to-date reference. The newest tagged release is 2.0 from 2024-02-11.

What licence is nbnet released under?

Zlib, with the LICENSE file at the root of the repository. The last push was on 2026-10-01, and the repository publishes releases 2.0, 1.1.3 and 1.1.2, with 3.0 still on a branch rather than as a tag.

Official sources

  1. Issues
  2. License: Zlib
  3. nathhB/nbnet on GitHub
  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/nathhb-nbnet.svg)](https://hysenlabs.com/projects/nathhb-nbnet)

Was this useful?