sokol README

The project's README from github.com/floooh/sokol. As one markdown file: README.md.



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

Sokol

See what's new (06-Sep-2026: new utility header sokol_cmdbuf.h)

Build Bindings build build OdinRustDlangC3

Core libraries

Utility libraries

'Official' Language Bindings

These are automatically updated on changes to the C headers:

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:

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:

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:

sokol_app.h

A minimal cross-platform application-wrapper library:

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"

static struct {
    sg_pipeline pip;
    sg_bindings bind;
    sg_pass_action pass_action;
} state;

static void init(void) {
    sg_setup(&(sg_desc){
        .environment = sglue_environment(),
        .logger.func = slog_func,
    });

    float vertices[] = {
         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 } }
    };
}

void frame(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();
}

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 = "Triangle",
        .icon.sokol_default = true,
        .logger.func = slog_func,
    };
}

sokol_audio.h

A minimal audio-streaming API:

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

// the sample callback, running in audio thread
static void stream_cb(float* buffer, int num_frames, int num_channels) {
    assert(1 == num_channels);
    static uint32_t count = 0;
    for (int i = 0; i < num_frames; i++) {
        buffer[i] = (count++ & (1<<3)) ? 0.5f : -0.5f;
    }
}

int main() {
    // init sokol-audio with default params
    saudio_setup(&(saudio_desc){
        .stream_cb = stream_cb,
        .logger.func = slog_func,
    });

    // run main loop
    ...

    // shutdown sokol-audio
    saudio_shutdown();
    return 0;

The same code using the push-model

#define BUF_SIZE (32)
int main() {
    // init sokol-audio with default params, no callback
    saudio_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 inefficient
    float buf[BUF_SIZE];
    int buf_pos = 0;
    uint32_t count = 0;

    // push samples from main loop
    bool done = false;
    while (!done) {
        // generate and push audio samples...
        int num_frames = saudio_expect();
        for (int i = 0; i < num_frames; i++) {
            // simple square wave generator
            buf[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-audio
    saudio_shutdown();
    return 0;
}

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"

static void response_callback(const sfetch_response*);

#define MAX_FILE_SIZE (1024*1024)
static uint8_t buffer[MAX_FILE_SIZE];

// application init
static void init(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...
static void frame(void) {
    ...
    // need to call sfetch_dowork() once per frame to 'turn the gears':
    sfetch_dowork();
    ...
}

// the response callback is where the interesting stuff happens:
static void response_callback(const sfetch_response_t* response) {
    if (response->fetched) {
        // data has been loaded into the provided buffer, do something
        // with the data...
        const void* data = response->buffer_ptr;
        uint64_t data_size = response->fetched_size;
    }
    // the finished flag is set both on success and failure
    if (response->failed) {
        // oops, something went wrong
        switch (response->error_code) {
            SFETCH_ERROR_FILE_NOT_FOUND: ...
            SFETCH_ERROR_BUFFER_TOO_SMALL: ...
            ...
        }
    }
}

// application shutdown
static void shutdown(void) {
    ...
    sfetch_shutdown();
    ...
}

sokol_time.h:

Simple cross-platform time measurement:

#include "sokol_time.h"
...
/* initialize sokol_time */
stm_setup();

/* take start timestamp */
uint64_t start = stm_now();

...some code to measure...

/* compute elapsed time */
uint64_t elapsed = stm_since(start);

/* convert to time units */
double seconds = stm_sec(elapsed);
double milliseconds = stm_ms(elapsed);
double microseconds = stm_us(elapsed);
double nanoseconds = stm_ns(elapsed);

/* difference between 2 time stamps */
uint64_t start = stm_now();
...
uint64_t end = stm_now();
uint64_t elapsed = stm_diff(end, start);

/* compute a 'lap time' (e.g. for fps) */
uint64_t last_time = 0;
while (!done) {
    ...render something...
    double frame_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"

int main(int argc, char* argv[]) {
    sargs_setup(&(sargs_desc){ .argc=argc, .argv=argv });
    if (sargs_exists("type")) {
        if (sargs_equals("type", "kc85_4")) {
            // start as KC85/4
        }
        else if (sargs_equals("type", "kc85_3")) {
            // start as KC85/3
        }
        else {
            // start as KC85/2
        }
    }
    sargs_shutdown();
    return 0;
}

See the sokol_args.h header for a more complete documentation, and the Tiny Emulators for more interesting usage examples.

sokol (floooh/sokol)

Searching Database…

10.4K ·GitHub
What is this?Sokol is a collection of minimal, cross-platform, standalone C header libraries for building apps and games: 3D graphics, windowing and input, audio, time, async file loading and argument parsing.
Is this one library or a set of separate pieces?A set of independent single-file headers, not one framework: sokol_gfx.h for rendering, sokol_app.h for window and input, plus audio, time, fetch and args headers — you take only what you need.
How do I get it into my project?Drop the header files into your project; in exactly one C or C++ file define SOKOL_IMPL before the include, and pick one rendering backend there, e.g. SOKOL_GLCORE, SOKOL_METAL or SOKOL_D3D11.
Do I need to install other libraries or SDKs first?No third-party libraries: the headers wrap platform APIs directly (GL, Metal, D3D11, WASAPI, CoreAudio, ALSA). Linux builds usually need the GL/X11 dev packages, and web builds compile with Emscripten.
What are the common pitfalls on a first build?Mostly backend mismatches: the SOKOL_IMPL file must select exactly one backend matching your shaders, macOS/iOS builds must compile that file as Objective-C (.m/.mm), and Emscripten WebGL2 needs -s USE_WEBGL2=1.
Which platforms can I target with it?sokol_app.h runs on Win32, macOS, Linux (X11), iOS, Android, UWP and WASM, and sokol_gfx.h adds WebGPU to the GL/Metal/D3D11 mix — WebAssembly is a first-class target.
Which 3D APIs does it wrap?sokol_gfx.h wraps GLES3/WebGL2, GL 3.3, D3D11, Metal, WebGPU and Vulkan, exposing buffers, images, shaders, pipeline-state objects and render passes.
Can I see it running somewhere before committing?Yes — the README links live WASM samples that run in the browser, plus a Doom Shareware port, a single-file Pacman clone in C99, and Steam games built with Sokol like Solar Storm and Spanking Runners.
Why is it written in C rather than C++?Per the README: C is easier to integrate with other languages and other projects, and adds only minimal size overhead to executables.
Can I use it from languages other than C and C++?Yes — official bindings are kept automatically in sync with the C headers for Zig, Odin, Nim, Rust, D, Jai and C3.
How do shaders work with sokol_gfx.h?The header does not cross-translate shader dialects; you use the official sokol-shdc command-line compiler, and its generated shader headers must match the backend you select at compile time.
What are similar projects?bgfx, filament, raylib, assimp, glfw, ps2-web-recomp, imgui, stb
Creator
floooh
Size
16.9 MB
Stars
10.4K
Updated
CommitOct 17
Age
9y
Commits · 90 Days