floooh/sokol
> Minimal STB-style single-file C headers that wrap modern 3D APIs (GL, Metal, D3D11, WebGPU), windowing, audio, and I/O — an anti-engine for people who want explicit control.
GitHub repo · Live WASM samples · License: Zlib
Overview
Sokol is a collection of standalone, cross-platform single-file C headers by Andre Weissflog (floooh), started in July 2017[^1]. The core is sokol_gfx.h, a thin wrapper over GL 3.3 / GLES3 / WebGL2, D3D11, Metal, and WebGPU that exposes buffers, images, samplers, shaders, pipeline-state objects, and render passes — a Metal-era explicit API projected onto older backends. Around it sit sokol_app.h (window, 3D context, input, unified entry point), plus small headers for audio streaming, async file/HTTP loading, timing, argument parsing, and logging[^2]. Each header can be used independently; there is no framework lock-in between them.
The defining tension is deliberate minimalism as a hard constraint. There is no runtime shader cross-compilation (shaders are compiled offline by the separate sokol-shdc tool[^3]), exactly one rendering backend is selected at compile time, resource pools are fixed-size at startup, and the API is single-threaded. In exchange you get libraries that compile in seconds, add tens of kilobytes to a binary, and treat WebAssembly as a first-class target — one of the project's stated founding motivations[^2]. C (not C++) is the implementation language specifically so that bindings for other languages can be generated automatically; official Zig, Odin, Nim, Rust, D, Jai, and C3 bindings are regenerated by CI on every header change[^2].
At ~10.1k stars and 656 forks after nine years, sokol is the established niche choice for emulators, demos, tools, and small-to-mid games rather than a mass-market engine. It is actively maintained (last push July 2026, with a swapchain-configuration update shipped 2026-07-02[^4]), but development is overwhelmingly a single-maintainer effort — a real bus-factor consideration for long-lived projects.
Getting Started
There is no package manager step: copy the headers into your project. One translation unit defines the implementation and backend[^5]:
// clear.c — minimal clear-screen app
#define SOKOL_IMPL
#define SOKOL_GLCORE // or SOKOL_D3D11, SOKOL_METAL, SOKOL_GLES3, SOKOL_WGPU
#include "sokol_app.h"
#include "sokol_gfx.h"
#include "sokol_log.h"
#include "sokol_glue.h"
static sg_pass_action pass_action;
static void init(void) {
sg_setup(&(sg_desc){ .environment = sglue_environment(), .logger.func = slog_func });
pass_action = (sg_pass_action){
.colors[0] = { .load_action = SG_LOADACTION_CLEAR,
.clear_value = { 0.2f, 0.3f, 0.3f, 1.0f } }
};
}
static void frame(void) {
sg_begin_pass(&(sg_pass){ .action = pass_action, .swapchain = sglue_swapchain() });
sg_end_pass();
sg_commit();
}
static void cleanup(void) { sg_shutdown(); }
sapp_desc sokol_main(int argc, char* argv[]) {
(void)argc; (void)argv;
return (sapp_desc){
.init_cb = init, .frame_cb = frame, .cleanup_cb = cleanup,
.width = 640, .height = 480, .window_title = "clear",
.icon.sokol_default = true, .logger.func = slog_func,
};
}
# Linux (X11/GL)
cc clear.c -o clear -lGL -lX11 -lXi -lXcursor -ldl -lpthread -lm
On macOS/iOS the implementation file must be compiled as Objective-C (.m/.mm) and link the frameworks listed in each header's doc comment[^5].
Architecture / How It Works
Header anatomy. Each header is declaration + implementation in one file, guarded by SOKOL_IMPL (STB convention). All configuration goes through C99 designated-initializer "desc" structs where zero means "sensible default" — the API is declarative structs in, opaque integer handles out.
sokol_gfx resource model. Resources (buffers, images, samplers, shaders, pipelines, attachments) live in fixed-size pools whose capacity is set once in sg_setup(). Handles are generation-counted indices, so use-after-destroy is detected rather than crashing. Rendering is organized as passes: sg_begin_pass → apply pipeline/bindings/uniforms → sg_draw → sg_end_pass → sg_commit. Pipeline-state objects bundle shader, vertex layout, and all render state up front — the D3D12/Metal design philosophy, implemented on top of D3D11 and GL by internal state caching.
Shader story. sokol_gfx deliberately does not translate shaders at runtime. The companion sokol-shdc compiler takes annotated GLSL and emits per-backend shader blobs plus a C header with reflection data (attribute slots, uniform block layouts) that plugs directly into sg_make_shader/sg_make_pipeline[^3]. This keeps the runtime tiny but adds an offline build step, and the shdc version must match the header version.
sokol_app inversion. You export sokol_main() returning a desc with init/frame/cleanup/event callbacks; the library owns the main loop and the 3D context. This is what makes the same code run on Win32, macOS, Linux/X11, iOS, Android, and Emscripten (where the frame callback maps onto the browser's animation loop). sokol_glue.h forwards the app's swapchain/environment into sokol_gfx, which is the only coupling point between the two headers.
Bindings pipeline. Because the public API is plain C structs and functions, a generator produces the official language bindings mechanically from the headers, and CI keeps all seven in lockstep with master[^2].
Production Notes
- No versioned releases. Sokol has no semver tags; breaking API updates land on master with migration notes in CHANGELOG.md[^4]. The sampler-object split (2023) and the render-pass unification (2024) each required mechanical porting of every call site. Pin a known-good commit and read the changelog before updating — "just pull master" is the main source of breakage reports.
- Backend is a compile-time contract.
SOKOL_GLCOREvsSOKOL_D3D11vsSOKOL_METALetc. must match across sokol_gfx, sokol_app, and your shdc-generated shaders in the same binary; mismatches produce link errors or garbage rendering rather than a clean diagnostic[^5]. There is no runtime backend switching. - Single-threaded by design. All sokol_gfx calls must come from one thread. There is no command-buffer recording from worker threads; if you need parallel submission, this is the wrong library.
- Fixed pools. Exceeding the pool sizes configured in
sg_setup()fails resource creation at runtime. Size them for peak usage; there is no dynamic growth. - Feature floor, not ceiling. The API targets the intersection of its backends, so it trails modern GPU features: storage buffers and compute support arrived only in the mid-2020s updates, and there is still no bindless, mesh shading, or ray tracing. Vulkan appears as a backend define only recently[^2]; D3D12 is absent.
- Web builds are the happy path but have sharp edges. Emscripten GLES3 needs
-s USE_WEBGL2=1; WebGPU builds need the matching Emscripten port flags described in the header comments[^5]. sokol_fetch requires callingsfetch_dowork()every frame — forgetting it silently stalls all loads. - Debugging. A validation layer is active in debug builds and error output goes through the logging callback — if you skip wiring
slog_func, failures are silent.sokol_gfx_imgui.hprovides a live resource/call inspector that is worth wiring into any nontrivial app.
When to Use / When Not
Use when:
- You want a modern explicit 3D API in C with the smallest possible footprint, especially targeting WebAssembly alongside native.
- You are building emulators, visualization tools, demos, or games where you own the engine layer (Dear ImGui integration is first-class).
- You work in Zig, Odin, Nim, Rust, D, Jai, or C3 and want maintained bindings that track upstream automatically.
- You value reading the entire graphics layer you ship — each header is self-documenting and auditable.
Avoid when:
- You need engine features: scene graph, asset pipeline, physics, or editor tooling. Sokol is a hardware abstraction, not an engine.
- You need multithreaded rendering, D3D12-class GPU features, or console platform support.
- You cannot absorb rolling breaking changes from an effectively single-maintainer project.
- You want runtime shader compilation or hot-reload without rebuilding through sokol-shdc.
Alternatives
- bkaradzic/bgfx — use instead when you need more backends (D3D12, Vulkan, consoles), multithreaded submission, and accept a much larger C++ codebase and build system.
- libsdl-org/SDL — use instead when you want a broader battle-tested platform layer (SDL3 includes its own GPU API) with a large community and versioned releases.
- raysan5/raylib — use instead when you want batteries-included immediate drawing for learning or jams rather than an explicit pipeline API.
- glfw/glfw — use instead when you only need windowing/input and want to program GL or Vulkan directly with no rendering abstraction.
- gfx-rs/wgpu — use instead for Rust-first projects wanting a safe WebGPU implementation rather than C bindings.
History
Sokol has no version numbers; history is a stream of dated CHANGELOG updates[^4].
| Update | Date | Notes | |--------|------|-------| | Initial release | 2017-07 | sokol_gfx.h, sokol_app.h, sokol_time.h; "A Tour of sokol_gfx.h" blog post[^1]. | | sokol_fetch.h | 2019 | Async file/HTTP streaming header added. | | Sampler objects | 2023 | Separate sg_sampler resources split out of images (breaking). | | WebGPU backend | 2023 | WebGPU joins GL/Metal/D3D11 in sokol_gfx.h. | | Render pass unification | 2024 | Single sg_begin_pass with swapchain param replaces default-pass API (breaking). | | Bindings cleanup | 2024-11 | sg_bindings rework, tighter sokol-shdc reflection integration (breaking). | | Advanced swapchain config | 2026-07-02 | Latest major update as of this writing[^4]. |
References
[^1]: Andre Weissflog, "A Tour of sokol_gfx.h" — 2017-07-29. https://floooh.github.io/2017/07/29/sokol-gfx-tour.html [^2]: sokol README — header list, language bindings, WASM motivation, backend defines. https://github.com/floooh/sokol#readme [^3]: sokol-shdc shader compiler documentation. https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md [^4]: sokol CHANGELOG. https://github.com/floooh/sokol/blob/master/CHANGELOG.md [^5]: sokol-samples, "How to build without a build system". https://github.com/floooh/sokol-samples#how-to-build-without-a-build-system
Tags
c, graphics, rendering, gamedev, cross-platform, single-header, webassembly, opengl, metal, d3d11, webgpu