Top Related Projects
A simple and easy-to-use library to enjoy videogames programming
Dear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies
A single-header ANSI C immediate mode cross-platform GUI library
A tiny immediate-mode UI library
Emscripten: An LLVM-to-WebAssembly Compiler
Simple Directmedia Layer
Quick Overview
Sokol is a minimalist, cross-platform library for developing graphical applications and games. It provides a set of single-file libraries that can be easily integrated into C and C++ projects, offering simple APIs for graphics, audio, and application lifecycle management across multiple platforms.
Pros
- Simple and lightweight, with each module contained in a single header file
- Cross-platform support for Windows, macOS, Linux, iOS, Android, and web browsers (via WebAssembly)
- Easy integration into existing projects without complex build systems
- Provides a consistent API across different platforms and rendering backends (OpenGL, Metal, D3D11, WebGL)
Cons
- Limited feature set compared to more comprehensive game engines or frameworks
- Requires more low-level programming knowledge compared to higher-level game development tools
- Documentation could be more extensive, especially for beginners
- May require additional libraries for more advanced functionality (e.g., physics, networking)
Code Examples
- Initializing a window and rendering context:
#define SOKOL_IMPL
#define SOKOL_GLCORE33
#include "sokol_app.h"
#include "sokol_gfx.h"
sapp_desc sokol_main(int argc, char* argv[]) {
return (sapp_desc){
.width = 800,
.height = 600,
.init_cb = init,
.frame_cb = frame,
.cleanup_cb = cleanup,
.window_title = "Sokol Example",
};
}
- Creating and rendering a triangle:
void init(void) {
sg_setup(&(sg_desc){0});
float vertices[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};
sg_buffer vbuf = sg_make_buffer(&(sg_buffer_desc){
.data = SG_RANGE(vertices)
});
state.pip = sg_make_pipeline(&(sg_pipeline_desc){
.shader = sg_make_shader(simple_shader_desc()),
.layout = {
.attrs[0] = { .format = SG_VERTEXFORMAT_FLOAT3 }
},
.primitive_type = SG_PRIMITIVETYPE_TRIANGLES
});
}
- Handling input events:
void input(const sapp_event* ev) {
if (ev->type == SAPP_EVENTTYPE_KEY_DOWN) {
if (ev->key_code == SAPP_KEYCODE_ESCAPE) {
sapp_request_quit();
}
}
}
Getting Started
To get started with Sokol, follow these steps:
- Download the required header files from the Sokol GitHub repository.
- Include the necessary headers in your project (e.g.,
sokol_app.h
,sokol_gfx.h
). - Implement the
sokol_main
function as the entry point for your application. - Define callbacks for initialization, frame rendering, and cleanup.
- Compile and link your project with the appropriate backend libraries (e.g., OpenGL, Metal).
Example minimal setup:
#define SOKOL_IMPL
#define SOKOL_GLCORE33
#include "sokol_app.h"
#include "sokol_gfx.h"
void init(void) {
sg_setup(&(sg_desc){0});
}
void frame(void) {
sg_begin_default_pass(&(sg_pass_action){0}, sapp_width(), sapp_height());
sg_end_pass();
sg_commit();
}
void cleanup(void) {
sg_shutdown();
}
sapp_desc sokol_main(int argc, char* argv[]) {
return (sapp_desc){
Competitor Comparisons
A simple and easy-to-use library to enjoy videogames programming
Pros of raylib
- More comprehensive feature set, including audio, 3D support, and built-in math functions
- Extensive documentation and examples, making it easier for beginners to get started
- Cross-platform support with official bindings for multiple programming languages
Cons of raylib
- Larger codebase and dependencies, which may increase compile times and binary size
- Less flexibility for integrating with existing projects or custom rendering pipelines
- Opinionated API design that may not suit all programming styles or project requirements
Code Comparison
raylib:
#include "raylib.h"
int main() {
InitWindow(800, 450, "raylib example");
while (!WindowShouldClose()) {
BeginDrawing();
ClearBackground(RAYWHITE);
DrawText("Hello, raylib!", 190, 200, 20, LIGHTGRAY);
EndDrawing();
}
CloseWindow();
return 0;
}
sokol:
#include "sokol_app.h"
#include "sokol_gfx.h"
sapp_desc sokol_main(int argc, char* argv[]) {
return (sapp_desc){
.init_cb = init,
.frame_cb = frame,
.cleanup_cb = cleanup,
.width = 800,
.height = 450,
.window_title = "sokol example",
};
}
Dear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies
Pros of ImGui
- Mature and widely adopted UI library with extensive documentation
- Rich set of UI elements and customization options
- Cross-platform support with multiple backend implementations
Cons of ImGui
- Focused solely on immediate mode GUI, less versatile for other graphics tasks
- Steeper learning curve for developers new to immediate mode GUI concepts
- Larger codebase and potentially higher memory footprint
Code Comparison
ImGui (C++):
ImGui::Begin("Hello, ImGui!");
if (ImGui::Button("Click me!"))
doSomething();
ImGui::End();
Sokol (C):
sg_begin_default_pass(&pass_action, width, height);
sgl_begin_default_pass(&pass_action, width, height);
// Draw your custom UI here
sgl_end();
sg_end_pass();
Key Differences
- ImGui is specifically designed for creating user interfaces, while Sokol is a more general-purpose graphics library
- Sokol provides a lower-level API for graphics programming, giving developers more control over rendering
- ImGui offers a higher-level abstraction for UI creation, making it easier to quickly prototype interfaces
Use Cases
- Choose ImGui for projects requiring complex user interfaces with minimal setup
- Opt for Sokol when building graphics applications that need fine-grained control over rendering and resource management
A single-header ANSI C immediate mode cross-platform GUI library
Pros of Nuklear
- More comprehensive UI toolkit with a wider range of pre-built widgets
- Highly customizable appearance and theming options
- Extensive documentation and examples
Cons of Nuklear
- Larger codebase and potentially higher memory footprint
- Steeper learning curve for beginners
- May require more setup and configuration
Code Comparison
Nuklear:
struct nk_context ctx;
nk_init_default(&ctx, 0);
if (nk_begin(&ctx, "Demo", nk_rect(50, 50, 200, 200), NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE|NK_WINDOW_MINIMIZABLE|NK_WINDOW_TITLE)) {
nk_layout_row_dynamic(&ctx, 30, 1);
if (nk_button_label(&ctx, "Button")) {
// Button clicked
}
}
nk_end(&ctx);
Sokol:
sg_pass_action pass_action = {0};
sg_begin_default_pass(&pass_action, sapp_width(), sapp_height());
sgl_defaults();
sgl_begin_triangles();
sgl_v2f_c3b( 0.0f, 0.5f, 255, 0, 0);
sgl_v2f_c3b(-0.5f, -0.5f, 0, 255, 0);
sgl_v2f_c3b( 0.5f, -0.5f, 0, 0, 255);
sgl_end();
sg_end_pass();
sg_commit();
A tiny immediate-mode UI library
Pros of microui
- Extremely lightweight and minimalistic (single-header library)
- Focused solely on immediate mode GUI, making it simpler to use for basic UI needs
- Easy to integrate into existing projects due to its small footprint
Cons of microui
- Limited feature set compared to Sokol's comprehensive toolkit
- Less active development and smaller community support
- Lacks cross-platform abstractions for graphics, audio, and input handling
Code Comparison
microui example:
mu_begin(ctx);
if (mu_begin_window(ctx, "My Window", mu_rect(10, 10, 200, 200))) {
mu_label(ctx, "Hello world!");
mu_end_window(ctx);
}
mu_end(ctx);
Sokol example (using sokol_gfx.h):
sg_begin_default_pass(&pass_action, width, height);
// ... rendering code ...
sg_end_pass();
sg_commit();
Summary
microui is a lightweight, single-header immediate mode GUI library, while Sokol is a more comprehensive toolkit for cross-platform game development. microui is simpler to integrate but has fewer features, whereas Sokol provides a wider range of functionality including graphics, audio, and input handling abstractions across multiple platforms.
Emscripten: An LLVM-to-WebAssembly Compiler
Pros of Emscripten
- Comprehensive toolchain for compiling C/C++ to WebAssembly
- Large ecosystem and community support
- Extensive documentation and examples
Cons of Emscripten
- Steeper learning curve for beginners
- Larger codebase and more complex setup
- May include unnecessary features for simple projects
Code Comparison
Emscripten (C++ to JavaScript):
#include <emscripten.h>
#include <stdio.h>
int main() {
printf("Hello, Emscripten!\n");
return 0;
}
Sokol (C header-only library):
#define SOKOL_IMPL
#include "sokol_app.h"
#include "sokol_gfx.h"
sapp_desc sokol_main(int argc, char* argv[]) {
return (sapp_desc){ .width = 640, .height = 480, .title = "Sokol App" };
}
Key Differences
- Emscripten focuses on compiling C/C++ to WebAssembly, while Sokol is a lightweight, header-only library for cross-platform development
- Emscripten provides a full toolchain, whereas Sokol offers minimal abstractions for common tasks
- Sokol is more suitable for small, performance-critical applications, while Emscripten excels in larger, complex projects requiring web integration
Simple Directmedia Layer
Pros of SDL
- More comprehensive feature set, including audio, input handling, and networking
- Wider platform support, including mobile and console platforms
- Larger community and ecosystem with extensive documentation and resources
Cons of SDL
- Larger codebase and potentially higher overhead
- More complex API with steeper learning curve
- Slower release cycle and potential for legacy code maintenance
Code Comparison
SDL:
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* window = SDL_CreateWindow("SDL Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, SDL_WINDOW_SHOWN);
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
Sokol:
sg_setup(&(sg_desc){0});
sapp_desc app_desc = {
.width = 640,
.height = 480,
.window_title = "Sokol Window",
.init_cb = init,
.frame_cb = frame,
};
sapp_run(&app_desc);
The code comparison shows that SDL requires more explicit initialization and management of window and renderer objects, while Sokol provides a more streamlined setup process with callback-based rendering.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
Simple
STB-style
cross-platform libraries for C and C++, written in C.
Sokol
See what's new (02-Sep-2024 in sokol_gfx.h, SG_FILTER_NONE has been removed)
Examples and Related Projects
-
Live Samples via WASM (source)
-
Doom Shareware ported to the Sokol headers (source)
-
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
-
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
-
A 'single-file' Pacman clone in C99, also available in Zig
-
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
-
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_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)
'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:
- 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
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"
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_vs_position].format = SG_VERTEXFORMAT_FLOAT3,
[ATTR_vs_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:
- 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 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.
Top Related Projects
A simple and easy-to-use library to enjoy videogames programming
Dear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies
A single-header ANSI C immediate mode cross-platform GUI library
A tiny immediate-mode UI library
Emscripten: An LLVM-to-WebAssembly Compiler
Simple Directmedia Layer
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot