GitHub - floooh/sokol: minimal cross-platform standalone C headers

GitHub

Simple

STB-style

cross-platform libraries for C and C++, written in C.

See what's new

(06-Sep-2026: new utility header sokol_cmdbuf.h)

Examples and Related Projects

Live Samples

via WASM (

source

)

Doom Shareware

ported to the Sokol headers (

source

)

Syntonic Dentiforms Redux

demo remaster by Aras Pranckevičius (

blog post

).

Everybody Wants to Crank the World

demo by Aras Pranckevičius, PC/web port via sokol (

source

).

sokol_gp.h

a 2D shape drawing library on top of sokol_gfx.h

NanoVG backend

a sokol_gfx backend for the amazing

NanoVG

library.

Dear ImGui starterkit

a self-contained starterkit for writing Dear ImGui apps in C.

qoiview

a basic viewer for the new QOI image file format

Tiny 8-bit emulators

A 'single-file'

Pacman clone in C99

, also available in

Zig

Soluna

, a framework to make 2D games in Lua by @cloudwu

Deep Future

: ...and a game implemented with Soluna

Solar Storm

, a turn-based scifi artillery game built with Odin and Sokol, released on Steam.

Spanking Runners (Samogonki)

, arcade racing in a bright and unusual world, released on Steam.

MEG-4

a virtual fantasy console emulator in C89, ported to sokol

A

Minigolf game

(

source

).

hIghQube

A game demo that used sokol rendering extensively

Senos

A music app that uses sokol as backend

'Dealer's Dungeon'

(

lower graphics quality

,

source

)

'Brick Warrior'

- a 1990's Breakout clone ported to Sokol :)

LearnOpenGL examples ported to sokol-gfx (may be outdated)

(

git repo

)

Command line tools

(shader compiler)

How to build without a build system

: useful details for integrating the Sokol headers into your own project with your favourite C/C++ build system

Core libraries

sokol_gfx.h

: 3D-API wrapper (GL/GLES3/WebGL2 + Metal + D3D11 + WebGPU)

sokol_app.h

: app framework wrapper (entry + window + 3D-context + input)

sokol_time.h

: time measurement

sokol_audio.h

: minimal buffer-streaming audio playback

sokol_fetch.h

: asynchronous data streaming from HTTP and local filesystem

sokol_args.h

: unified cmdline/URL arg parser for web and native apps

sokol_log.h

: provides a standard logging callback for the other sokol headers

Utility libraries

sokol_imgui.h

: sokol_gfx.h rendering backend for

Dear ImGui

sokol_nuklear.h

: sokol_gfx.h rendering backend for

Nuklear

sokol_gl.h

: OpenGL 1.x style immediate-mode rendering API on top of sokol_gfx.h

sokol_fontstash.h

: sokol_gl.h rendering backend for

fontstash

sokol_gfx_imgui.h

: debug-inspection UI for sokol_gfx.h (implemented with Dear ImGui)

sokol_app_imgui.h

: debug-inspection UI for sokol_app.h (implemented with Dear ImGui)

sokol_debugtext.h

: a simple text renderer using vintage home computer fonts

sokol_memtrack.h

: easily track memory allocations in sokol headers

sokol_shape.h

: generate simple shapes and plug them into sokol-gfx resource creation structs

sokol_color.h

: X11 style color constants and functions for creating sg_color objects

sokol_spine.h

: a sokol-style wrapper around the Spine C runtime (

http://en.esotericsoftware.com/spine-in-depth

)

sokol_letterbox.h

: compute viewport params for rendering fixed-aspect-ratio content in a variable-aspect-ratio canvas

sokol_framebuffer.h

: provides CPU-framebuffers rendered via sokol_gfx.h

sokol_cmdbuf.h

record/replay for sokol_gfx.h apply/draw/dispatch

'Official' Language Bindings

These are automatically updated on changes to the C headers:

sokol-zig

sokol-odin

sokol-nim

sokol-rust

sokol-d

sokol-jai

sokol-c3

Notes

WebAssembly is a 'first-class citizen', one important motivation for the Sokol headers is to provide a collection of cross-platform APIs with a minimal footprint on the web platform while still being useful.

The core headers are standalone and can be used independently from each other.

Why C:

easier integration with other languages

easier integration into other projects

adds only minimal size overhead to executables

A blog post with more background info:

A Tour of sokol_gfx.h

Quick build troubleshooting

When a first Sokol integration fails to compile or link, the problem is usually one of the following configuration mismatches:

Define SOKOL_IMPL (or the per-header SOKOL_*_IMPL define) in exactly one C or C++ translation unit before including the Sokol headers. Other files should include the headers without the implementation define.

Select exactly one rendering backend in that same implementation translation unit, for example SOKOL_GLCORE, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_VULKAN. The backend define must match the shader code and any window/context setup code you use.

If sokol_gfx.h and sokol_app.h are used together, use the same backend define for both headers. sokol_app.h creates the matching window and 3D API context; sokol_glue.h then forwards the platform-specific environment via sglue_environment() and sglue_swapchain().

macOS and iOS builds which use sokol_app.h or Metal must compile the implementation file as Objective-C or Objective-C++ (.m or .mm, or the equivalent compiler flag), and link the frameworks listed in the header docs.

Emscripten WebGL2 builds require the linker option -s USE_WEBGL2=1 when using SOKOL_GLES3. WebGPU builds use SOKOL_WGPU and need the WebGPU port/options described in the sokol_gfx.h header comments.

On Linux, OpenGL builds commonly need the GL/X11 development packages installed by the platform package manager. SOKOL_GLCORE uses GLX by default; define SOKOL_FORCE_EGL if your build intentionally uses EGL instead.

On Windows, MSVC and Clang builds usually pick up common system libraries via in-source #pragma comment(lib, ...) directives. MinGW/MSYS2 builds may need explicit linker flags such as -ld3d11 for SOKOL_D3D11.

For shader-related errors, confirm that generated shader headers come from a sokol-shdc version compatible with the Sokol headers and were generated for the same backend(s) selected at compile time.

For exact per-header compiler and linker requirements, see the comment block at the top of each Sokol header and the build notes in

sokol-samples

.

sokol_gfx.h:

simple, modern wrapper around GLES3/WebGL2, GL3.3, D3D11, Metal, and WebGPU

buffers, images, shaders, pipeline-state-objects and render-passes

does not handle window creation or 3D API context initialization

does not provide shader dialect cross-translation (BUT there's now an 'official' shader-cross-compiler solution which seamlessly integrates with sokol_gfx.h and IDEs:

see here for details

sokol_app.h

A minimal cross-platform application-wrapper library:

unified application entry

single window or canvas for 3D rendering

3D context initialization

event-based keyboard, mouse and touch input

supported platforms: Win32, MacOS, Linux (X11), iOS, WASM, Android, UWP

supported 3D-APIs: GL3.3 (GLX/WGL), Metal, D3D11, GLES3/WebGL2

The vanilla Hello-Triangle using sokol_gfx.h, sokol_app.h and the sokol-shdc shader compiler (shader code not shown):

#include"sokol_app.h"#include"sokol_gfx.h"#include"sokol_log.h"#include"sokol_glue.h"#include"triangle-sapp.glsl.h"staticstruct { sg_pipelinepip; sg_bindingsbind; sg_pass_actionpass_action; } state; staticvoidinit(void) { sg_setup(&(sg_desc){ .environment=sglue_environment(), .logger.func=slog_func, }); floatvertices[] = { 0.0f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f }; state.bind.vertex_buffers[0] =sg_make_buffer(&(sg_buffer_desc){ .data=SG_RANGE(vertices), }); state.pip=sg_make_pipeline(&(sg_pipeline_desc){ .shader=sg_make_shader(triangle_shader_desc(sg_query_backend())), .layout= { .attrs= { [ATTR_triangle_position].format=SG_VERTEXFORMAT_FLOAT3, [ATTR_triangle_color0].format=SG_VERTEXFORMAT_FLOAT4 } }, }); state.pass_action= (sg_pass_action) { .colors[0] = { .load_action=SG_LOADACTION_CLEAR, .clear_value={0.0f, 0.0f, 0.0f, 1.0f } } }; } voidframe(void) { sg_begin_pass(&(sg_pass){ .action=state.pass_action, .swapchain=sglue_swapchain() }); sg_apply_pipeline(state.pip); sg_apply_bindings(&state.bind); sg_draw(0, 3, 1); sg_end_pass(); sg_commit(); } voidcleanup(void) { sg_shutdown(); } sapp_descsokol_main(intargc, char*argv[]) { (void)argc; (void)argv; return (sapp_desc){ .init_cb=init, .frame_cb=frame, .cleanup_cb=cleanup, .width=640, .height=480, .window_title="Triangle", .icon.sokol_default= true, .logger.func=slog_func, }; }sokol_audio.h

A minimal audio-streaming API:

you provide a mono- or stereo-stream of 32-bit float samples which sokol_audio.h forwards into platform-specific backends

two ways to provide the data: directly fill backend audio buffer from your callback function running in the audio thread

alternatively push small packets of audio data from your main loop, or a separate thread created by you

platform backends: Windows: WASAPI

macOS/iOS: CoreAudio

Linux: ALSA

emscripten: WebAudio + ScriptProcessorNode (doesn't use the emscripten-provided OpenAL or SDL Audio wrappers)

A simple mono square-wave generator using the callback model:

// the sample callback, running in audio threadstaticvoidstream_cb(float*buffer, intnum_frames, intnum_channels) { assert(1==num_channels); staticuint32_tcount=0; for (inti=0; i<num_frames; i++) { buffer[i] = (count++& (1<<3)) ? 0.5f : -0.5f; } } intmain() { // init sokol-audio with default paramssaudio_setup(&(saudio_desc){ .stream_cb=stream_cb, .logger.func=slog_func, }); // run main loop ... // shutdown sokol-audiosaudio_shutdown(); return0;The same code using the push-model

#defineBUF_SIZE (32) intmain() { // init sokol-audio with default params, no callbacksaudio_setup(&(saudio_desc){ .logger.func=slog_func, }); assert(saudio_channels() ==1); // a small intermediate buffer so we don't need to push// individual samples, which would be quite inefficientfloatbuf[BUF_SIZE]; intbuf_pos=0; uint32_tcount=0; // push samples from main loopbooldone= false; while (!done) { // generate and push audio samples...intnum_frames=saudio_expect(); for (inti=0; i<num_frames; i++) { // simple square wave generatorbuf[buf_pos++] = (count++& (1<<3)) ? 0.5f : -0.5f; if (buf_pos==BUF_SIZE) { buf_pos=0; saudio_push(buf, BUF_SIZE); } } // handle other per-frame stuff... ... } // shutdown sokol-audiosaudio_shutdown(); return0; }sokol_fetch.h

Load entire files, or stream data asynchronously over HTTP (emscripten/wasm) or the local filesystem (all native platforms).

Simple C99 example loading a file into a static buffer:

#include"sokol_fetch.h"#include"sokol_log.h"staticvoidresponse_callback(constsfetch_response*); #defineMAX_FILE_SIZE (1024*1024) staticuint8_tbuffer[MAX_FILE_SIZE]; // application initstaticvoidinit(void) { ... // setup sokol-fetch with default config:sfetch_setup(&(sfetch_desc_t){ .logger.func=slog_func }); // start loading a file into a statically allocated buffer:sfetch_send(&(sfetch_request_t){ .path="hello_world.txt", .callback=response_callback .buffer_ptr=buffer, .buffer_size=sizeof(buffer) }); } // per frame...staticvoidframe(void) { ... // need to call sfetch_dowork() once per frame to 'turn the gears':sfetch_dowork(); ... } // the response callback is where the interesting stuff happens:staticvoidresponse_callback(constsfetch_response_t*response) { if (response->fetched) { // data has been loaded into the provided buffer, do something// with the data...constvoid*data=response->buffer_ptr; uint64_tdata_size=response->fetched_size; } // the finished flag is set both on success and failureif (response->failed) { // oops, something went wrongswitch (response->error_code) { SFETCH_ERROR_FILE_NOT_FOUND: ... SFETCH_ERROR_BUFFER_TOO_SMALL: ... ... } } } // application shutdownstaticvoidshutdown(void) { ... sfetch_shutdown(); ... }sokol_time.h:

Simple cross-platform time measurement:

#include"sokol_time.h" ... /* initialize sokol_time */stm_setup(); /* take start timestamp */uint64_tstart=stm_now(); ...somecodetomeasure... /* compute elapsed time */uint64_telapsed=stm_since(start); /* convert to time units */doubleseconds=stm_sec(elapsed); doublemilliseconds=stm_ms(elapsed); doublemicroseconds=stm_us(elapsed); doublenanoseconds=stm_ns(elapsed); /* difference between 2 time stamps */uint64_tstart=stm_now(); ... uint64_tend=stm_now(); uint64_telapsed=stm_diff(end, start); /* compute a 'lap time' (e.g. for fps) */uint64_tlast_time=0; while (!done) { ...rendersomething... doubleframe_time_ms=stm_ms(stm_laptime(&last_time)); }sokol_args.h

Unified argument parsing for web and native apps. Uses argc/argv on native platforms and the URL query string on the web.

Example URL with one arg:

https://floooh.github.io/tiny8bit/kc85.html?type=kc85_4

The same as command line app:

kc85 type=kc85_4

Parsed like this:

#include"sokol_args.h"intmain(intargc, char*argv[]) { sargs_setup(&(sargs_desc){ .argc=argc, .argv=argv }); if (sargs_exists("type")) { if (sargs_equals("type", "kc85_4")) { // start as KC85/4 } elseif (sargs_equals("type", "kc85_3")) { // start as KC85/3 } else { // start as KC85/2 } } sargs_shutdown(); return0; }See the sokol_args.h header for a more complete documentation, and the

Tiny Emulators

for more interesting usage examples.