Sokol Module
GX includes a Sokol module for cross-platform graphics and windowing. Sokol is a
set of single-header C libraries providing a platform-agnostic graphics API that
compiles to OpenGL, Direct3D 11, Metal, WebGL2, and WebGPU. Unlike raylib’s
polling game loop, Sokol uses a callback-based architecture — you provide
init, frame, cleanup, and event functions.
The module has four sub-modules:
| Module | Header | Purpose |
|---|---|---|
sokol.app | sokol_app.h | Window creation, event loop, input |
sokol.gfx | sokol_gfx.h | Low-level GPU rendering (buffers, shaders, pipelines) |
sokol.gp | sokol_gp.h | High-level 2D drawing (rects, triangles, lines, textures) |
sokol.glue | sokol_glue.h | Bridges sokol.app and sokol.gfx |
Prerequisites
The module ships with all Sokol headers bundled — no external dependencies needed.
The C source is compiled automatically via @cfile. On Windows, links against
opengl32, gdi32, user32, shell32 automatically.
Usage
import sokol.app // window + events
import sokol.gfx // graphics API
import sokol.glue // app↔gfx bridge
import sokol.gp // 2D drawing (optional)
Build with -I modules:
gx myapp.gx -I modules -o myapp.exe
Quick Example — Animated Shapes
import sokol.app
import sokol.gfx
import sokol.glue
import sokol.gp
import math.scalar
var g_time: f32 = 0.0
@c_abi fn init() {
var gfx_desc: sg_desc
gfx_desc.environment = sglue_environment()
sg_setup(&gfx_desc)
var gp_desc: sgp_desc
sgp_setup(&gp_desc)
}
@c_abi fn frame() {
var w = sapp_width()
var h = sapp_height()
g_time = g_time + 0.016
sgp_begin(w, h)
sgp_viewport(0, 0, w, h)
sgp_project(0.0, 800.0, 0.0, 600.0)
// Dark background
sgp_set_color(0.1, 0.1, 0.15, 1.0)
sgp_clear()
// Pulsing red rectangle
var pulse = sinf(g_time * 3.0) * 20.0
sgp_set_color(0.9, 0.2, 0.2, 1.0)
sgp_draw_filled_rect(100.0 - pulse, 100.0 - pulse, 200.0 + pulse * 2.0, 150.0 + pulse * 2.0)
// Spinning triangle
var cx = 500.0
var cy = 300.0
sgp_set_blend_mode(BLENDMODE_BLEND)
sgp_set_color(0.2, 0.6, 0.9, 0.8)
sgp_draw_filled_triangle(
cx + cosf(g_time) * 80.0, cy + sinf(g_time) * 80.0,
cx + cosf(g_time + 2.094) * 80.0, cy + sinf(g_time + 2.094) * 80.0,
cx + cosf(g_time + 4.189) * 80.0, cy + sinf(g_time + 4.189) * 80.0
)
// Flush 2D draws into Sokol GFX pass
var pass: sg_pass
pass.swapchain = sglue_swapchain()
sg_begin_pass(&pass)
sgp_flush()
sgp_end()
sg_end_pass()
sg_commit()
}
@c_abi fn event(ev: const *sapp_event) {
if (ev.type == EVENTTYPE_KEY_DOWN) {
if (ev.key_code == KEYCODE_ESCAPE) { sapp_quit() }
}
}
@c_abi fn cleanup() {
sgp_shutdown()
sg_shutdown()
}
fn main() {
var desc: sapp_desc
desc.init_cb = init
desc.frame_cb = frame
desc.event_cb = event
desc.cleanup_cb = cleanup
desc.width = 800
desc.height = 600
desc.window_title = "GX + Sokol"
sapp_run(&desc)
}
Callback Architecture
Sokol uses an event-driven model. You define four callback functions marked
@c_abi and pass them via sapp_desc:
@c_abi fn init() // called once at startup
@c_abi fn frame() // called every frame
@c_abi fn event(ev: const *sapp_event) // called on input/window events
@c_abi fn cleanup() // called on shutdown
The @c_abi attribute is required because Sokol calls these from C code.
Comparison with Raylib
| Raylib | Sokol | |
|---|---|---|
| Loop style | Polling (while !WindowShouldClose) | Callback (init/frame/cleanup) |
| Input | IsKeyPressed() per frame | Event callback with sapp_event |
| 2D drawing | DrawRectangle, DrawText, etc. | sgp_draw_filled_rect, etc. |
| 3D drawing | DrawCube, LoadModel, etc. | Manual shaders + pipelines |
| Text | Built-in font rendering | No built-in text |
| Setup | InitWindow() one-liner | sapp_desc + sg_setup + sgp_setup |
| Web support | Limited | Full (WebGL2/WebGPU via Emscripten) |
App (sokol.app) — Window & Events
Application Descriptor
var desc: sapp_desc
desc.init_cb = init // fn()
desc.frame_cb = frame // fn()
desc.event_cb = event // fn(const *sapp_event)
desc.cleanup_cb = cleanup // fn()
desc.width = 800 // initial window width
desc.height = 600 // initial window height
desc.sample_count = 4 // MSAA samples (1 = off)
desc.swap_interval = 1 // V-sync (1 = 60fps on 60Hz)
desc.high_dpi = true // HiDPI support
desc.fullscreen = false // fullscreen mode
desc.window_title = "My App" // window title
sapp_run(&desc)
Window Functions
| Function | Signature | Description |
|---|---|---|
sapp_run | (const *sapp_desc) | Start the application event loop |
sapp_quit | () | Request application exit |
sapp_width | () → c_int | Current window width in pixels |
sapp_height | () → c_int | Current window height in pixels |
sapp_frame_count | () → u64 | Total frames rendered |
sapp_frame_duration | () → f64 | Last frame time in seconds |
sapp_sample_count | () → c_int | Current MSAA sample count |
sapp_color_format | () → c_int | Framebuffer color format |
sapp_depth_format | () → c_int | Framebuffer depth format |
Event Handling
Events arrive via the event_cb callback:
@c_abi fn event(ev: const *sapp_event) {
if (ev.type == EVENTTYPE_KEY_DOWN) {
if (ev.key_code == KEYCODE_W) { move_forward() }
if (ev.key_code == KEYCODE_ESCAPE) { sapp_quit() }
}
if (ev.type == EVENTTYPE_MOUSE_DOWN) {
if (ev.mouse_button == MOUSEBUTTON_LEFT) {
shoot(ev.mouse_x, ev.mouse_y)
}
}
if (ev.type == EVENTTYPE_MOUSE_MOVE) {
look_at(ev.mouse_dx, ev.mouse_dy)
}
if (ev.type == EVENTTYPE_MOUSE_SCROLL) {
zoom(ev.scroll_y)
}
}
Event Struct (sapp_event)
| Field | Type | Description |
|---|---|---|
frame_count | u64 | Frame number when event occurred |
type | sapp_event_type | Event type enum |
key_code | sapp_keycode | Keyboard scancode |
char_code | u32 | Unicode character (for CHAR events) |
key_repeat | c_bool | True if key is auto-repeating |
modifiers | u32 | Modifier key bitmask |
mouse_button | sapp_mousebutton | Which mouse button |
mouse_x, mouse_y | f32 | Mouse position |
mouse_dx, mouse_dy | f32 | Mouse delta since last event |
scroll_x, scroll_y | f32 | Scroll wheel delta |
num_touches | c_int | Number of active touches |
touches | sapp_touchpoint[8] | Touch point array |
window_width, window_height | c_int | Window size |
framebuffer_width, framebuffer_height | c_int | Framebuffer size |
Event Types
| Constant | Description |
|---|---|
EVENTTYPE_KEY_DOWN | Key pressed |
EVENTTYPE_KEY_UP | Key released |
EVENTTYPE_CHAR | Character typed (Unicode) |
EVENTTYPE_MOUSE_DOWN | Mouse button pressed |
EVENTTYPE_MOUSE_UP | Mouse button released |
EVENTTYPE_MOUSE_SCROLL | Mouse wheel scrolled |
EVENTTYPE_MOUSE_MOVE | Mouse moved |
EVENTTYPE_MOUSE_ENTER | Mouse entered window |
EVENTTYPE_MOUSE_LEAVE | Mouse left window |
EVENTTYPE_RESIZED | Window resized |
EVENTTYPE_FOCUSED | Window gained focus |
EVENTTYPE_UNFOCUSED | Window lost focus |
EVENTTYPE_QUIT_REQUESTED | Close button clicked |
Keyboard Constants
All standard keycodes: KEYCODE_A through KEYCODE_Z, KEYCODE_0 through
KEYCODE_9, KEYCODE_SPACE, KEYCODE_ESCAPE, KEYCODE_ENTER, KEYCODE_TAB,
KEYCODE_BACKSPACE, KEYCODE_DELETE, arrow keys (KEYCODE_UP, KEYCODE_DOWN,
KEYCODE_LEFT, KEYCODE_RIGHT), function keys (KEYCODE_F1 through
KEYCODE_F25), modifiers (KEYCODE_LEFT_SHIFT, KEYCODE_LEFT_CONTROL,
KEYCODE_LEFT_ALT), numpad (KEYCODE_KP_0 through KEYCODE_KP_9).
Mouse Button Constants
MOUSEBUTTON_LEFT, MOUSEBUTTON_RIGHT, MOUSEBUTTON_MIDDLE.
GFX (sokol.gfx) — GPU Rendering
The low-level graphics API for creating buffers, shaders, and pipelines.
Initialization
@c_abi fn init() {
var gfx_desc: sg_desc
gfx_desc.environment = sglue_environment()
sg_setup(&gfx_desc)
}
Render Pass
A render pass clears the framebuffer and draws geometry:
@c_abi fn frame() {
var pass: sg_pass
pass.swapchain = sglue_swapchain()
pass.action.colors[0].load_action = 1 // SG_LOADACTION_CLEAR
pass.action.colors[0].clear_value.r = 0.1
pass.action.colors[0].clear_value.g = 0.1
pass.action.colors[0].clear_value.b = 0.15
pass.action.colors[0].clear_value.a = 1.0
sg_begin_pass(&pass)
// ... draw ...
sg_end_pass()
sg_commit()
}
Core Functions
| Function | Signature | Description |
|---|---|---|
sg_setup | (const *sg_desc) | Initialize graphics system |
sg_shutdown | () | Shutdown graphics system |
sg_begin_pass | (const *sg_pass) | Begin a render pass |
sg_end_pass | () | End current render pass |
sg_commit | () | Submit all commands to GPU |
sg_apply_pipeline | (sg_pipeline) | Set active pipeline |
sg_apply_bindings | (const *sg_bindings) | Set active buffers/textures |
sg_draw | (c_int, c_int, c_int) | Draw: base_element, num_elements, num_instances |
sg_update_buffer | (sg_buffer, const *sg_range) | Update buffer data |
Resource Creation
| Function | Signature | Description |
|---|---|---|
sg_make_buffer | (const *sg_buffer_desc) → sg_buffer | Create vertex/index buffer |
sg_make_shader | (const *sg_shader_desc) → sg_shader | Create shader program |
sg_make_pipeline | (const *sg_pipeline_desc) → sg_pipeline | Create pipeline |
sg_destroy_buffer | (sg_buffer) | Free buffer |
sg_destroy_shader | (sg_shader) | Free shader |
sg_destroy_pipeline | (sg_pipeline) | Free pipeline |
Custom Shader Example
For advanced rendering (e.g. a custom particle system), you create shaders, pipelines, and vertex buffers manually:
// Vertex data for a colored triangle
var vertices: f32[15] = [
// x, y, r, g, b
0.0, 0.5, 1.0, 0.0, 0.0,
0.5, -0.5, 0.0, 1.0, 0.0,
-0.5, -0.5, 0.0, 0.0, 1.0
]
// Create vertex buffer
var buf_desc: sg_buffer_desc
buf_desc.data.ptr = &vertices
buf_desc.data.size = 60 // 15 floats * 4 bytes
var vbuf = sg_make_buffer(&buf_desc)
// Create shader with inline GLSL
var shd_desc: sg_shader_desc
shd_desc.vertex_func.source = "
#version 330
layout(location=0) in vec2 pos;
layout(location=1) in vec3 col;
out vec3 v_color;
void main() { gl_Position = vec4(pos, 0.0, 1.0); v_color = col; }
"
shd_desc.fragment_func.source = "
#version 330
in vec3 v_color;
out vec4 frag_color;
void main() { frag_color = vec4(v_color, 1.0); }
"
shd_desc.attrs[0].glsl_name = "pos"
shd_desc.attrs[1].glsl_name = "col"
var shd = sg_make_shader(&shd_desc)
// Create pipeline
var pip_desc: sg_pipeline_desc
pip_desc.shader = shd
pip_desc.layout.attrs[0].format = 3 // SG_VERTEXFORMAT_FLOAT2
pip_desc.layout.attrs[1].format = 5 // SG_VERTEXFORMAT_FLOAT3
var pip = sg_make_pipeline(&pip_desc)
// Draw each frame
sg_apply_pipeline(pip)
var bind: sg_bindings
bind.vertex_buffers[0] = vbuf
sg_apply_bindings(&bind)
sg_draw(0, 3, 1)
Handle Types
All GPU resources are opaque 32-bit handles:
| Type | Description |
|---|---|
sg_buffer | Vertex or index buffer |
sg_image | Texture image |
sg_sampler | Texture sampler |
sg_shader | Shader program |
sg_pipeline | Render pipeline |
sg_attachments | Render target attachments |
GP (sokol.gp) — 2D Drawing
A high-level 2D drawing API built on top of sokol.gfx. Similar in concept to
raylib’s shape drawing but with explicit batching and state management.
Initialization
@c_abi fn init() {
// Initialize gfx first
var gfx_desc: sg_desc
gfx_desc.environment = sglue_environment()
sg_setup(&gfx_desc)
// Then initialize 2D painter
var gp_desc: sgp_desc
sgp_setup(&gp_desc)
}
Frame Structure
@c_abi fn frame() {
var w = sapp_width()
var h = sapp_height()
sgp_begin(w, h) // begin 2D draw batch
sgp_viewport(0, 0, w, h) // set viewport
sgp_project(0.0, 800.0, 0.0, 600.0) // coordinate system
sgp_set_color(0.1, 0.1, 0.1, 1.0)
sgp_clear() // clear background
// ... draw shapes ...
// Flush into Sokol GFX pass
var pass: sg_pass
pass.swapchain = sglue_swapchain()
sg_begin_pass(&pass)
sgp_flush() // submit 2D draws
sgp_end() // end 2D batch
sg_end_pass()
sg_commit()
}
Drawing Primitives
| Function | Signature | Description |
|---|---|---|
sgp_clear | () | Clear framebuffer to current color |
sgp_draw_point | (f32, f32) | Single point |
sgp_draw_points | (const *sgp_vec2, u32) | Multiple points |
sgp_draw_line | (f32, f32, f32, f32) | Line from (ax,ay) to (bx,by) |
sgp_draw_lines | (const *sgp_line, u32) | Multiple lines |
sgp_draw_lines_strip | (const *sgp_vec2, u32) | Connected line strip |
sgp_draw_filled_triangle | (f32, f32, f32, f32, f32, f32) | Filled triangle |
sgp_draw_filled_triangles | (const *sgp_triangle, u32) | Multiple triangles |
sgp_draw_filled_triangles_strip | (const *sgp_vec2, u32) | Triangle strip |
sgp_draw_filled_rect | (f32, f32, f32, f32) | Filled rectangle |
sgp_draw_filled_rects | (const *sgp_rect, u32) | Multiple rectangles |
sgp_draw_textured_rect | (c_int, sgp_rect, sgp_rect) | Textured rectangle |
sgp_draw_textured_rects | (c_int, const *sgp_textured_rect, u32) | Multiple textured rects |
Drawing a Game HUD
// Health bar background
sgp_set_color(0.3, 0.0, 0.0, 0.8)
sgp_draw_filled_rect(20.0, 20.0, 200.0, 20.0)
// Health bar fill (proportional)
var health_pct = 0.75 // 75% health
sgp_set_color(0.0, 0.9, 0.0, 0.9)
sgp_draw_filled_rect(20.0, 20.0, 200.0 * health_pct, 20.0)
// Minimap border
sgp_set_color(0.5, 0.5, 0.5, 0.8)
sgp_draw_filled_rect(600.0, 20.0, 164.0, 164.0) // border
sgp_set_color(0.1, 0.1, 0.15, 0.9)
sgp_draw_filled_rect(602.0, 22.0, 160.0, 160.0) // inner
// Player dot on minimap
sgp_set_color(0.0, 1.0, 0.0, 1.0)
sgp_draw_filled_rect(680.0, 100.0, 4.0, 4.0)
// Enemy dots
sgp_set_color(1.0, 0.0, 0.0, 1.0)
sgp_draw_filled_rect(650.0, 80.0, 3.0, 3.0)
sgp_draw_filled_rect(700.0, 130.0, 3.0, 3.0)
Color & Blending
| Function | Signature | Description |
|---|---|---|
sgp_set_color | (f32, f32, f32, f32) | Set RGBA color (0.0-1.0) |
sgp_reset_color | () | Reset to white |
sgp_set_blend_mode | (sgp_blend_mode) | Set blending mode |
sgp_reset_blend_mode | () | Reset to no blending |
Blend Modes:
| Constant | Description |
|---|---|
BLENDMODE_NONE | No blending (replace) |
BLENDMODE_BLEND | Standard alpha blending |
BLENDMODE_ADD | Additive (glow effects) |
BLENDMODE_MUL | Multiply (shadows) |
BLENDMODE_BLEND_PREMULTIPLIED | Premultiplied alpha |
Transforms
Apply 2D transforms to subsequent draw calls:
| Function | Signature | Description |
|---|---|---|
sgp_push_transform | () | Save transform state |
sgp_pop_transform | () | Restore transform state |
sgp_reset_transform | () | Reset to identity |
sgp_translate | (f32, f32) | Translate |
sgp_rotate | (f32) | Rotate (radians) |
sgp_rotate_at | (f32, f32, f32) | Rotate around point |
sgp_scale | (f32, f32) | Scale |
sgp_scale_at | (f32, f32, f32, f32) | Scale around point |
// Draw a rotated rectangle (like a spinning pickup item)
sgp_push_transform()
sgp_rotate_at(g_time * 2.0, 400.0, 300.0) // spin around center
sgp_set_color(1.0, 0.8, 0.0, 1.0)
sgp_draw_filled_rect(385.0, 285.0, 30.0, 30.0)
sgp_pop_transform()
Textures
| Function | Signature | Description |
|---|---|---|
sgp_set_image | (c_int, sg_image) | Bind texture to channel |
sgp_unset_image | (c_int) | Unbind texture |
sgp_reset_image | (c_int) | Reset to default white |
sgp_set_sampler | (c_int, sg_sampler) | Set sampler for channel |
sgp_reset_sampler | (c_int) | Reset sampler |
// Draw a textured sprite
sgp_set_image(0, sprite_texture)
sgp_set_color(1.0, 1.0, 1.0, 1.0) // no tint
var dst: sgp_rect
dst.x = player_x; dst.y = player_y; dst.w = 64.0; dst.h = 64.0
var src: sgp_rect
src.x = 0.0; src.y = 0.0; src.w = 1.0; src.h = 1.0 // full texture
sgp_draw_textured_rect(0, dst, src)
sgp_unset_image(0)
Viewport & Scissor
| Function | Signature | Description |
|---|---|---|
sgp_viewport | (c_int, c_int, c_int, c_int) | Set rendering viewport |
sgp_reset_viewport | () | Reset to full framebuffer |
sgp_project | (f32, f32, f32, f32) | Set orthographic projection |
sgp_reset_project | () | Reset projection |
sgp_scissor | (c_int, c_int, c_int, c_int) | Set scissor clip region |
sgp_reset_scissor | () | Disable scissor |
// Draw only within a UI panel region
sgp_scissor(20, 100, 300, 400) // clip to panel bounds
// ... draw panel contents ...
sgp_reset_scissor()
Batch Control
| Function | Description |
|---|---|
sgp_begin(w, h) | Start a 2D draw batch |
sgp_flush() | Submit all queued draws to Sokol GFX |
sgp_end() | End the 2D batch |
All sgp_draw_* calls are batched internally. sgp_flush() submits them
as Sokol GFX draw commands inside a render pass.
Error Handling
| Function | Signature | Description |
|---|---|---|
sgp_is_valid | () → c_bool | Check if GP initialized |
sgp_get_last_error | () → sgp_error | Get last error |
sgp_get_error_message | (sgp_error) → cstr | Error as string |
Glue (sokol.glue) — App/GFX Bridge
Two functions that connect sokol.app to sokol.gfx:
| Function | Signature | Description |
|---|---|---|
sglue_environment | () → sg_environment | Get platform environment for sg_setup |
sglue_swapchain | () → sg_swapchain | Get default framebuffer for sg_begin_pass |
// In init:
var desc: sg_desc
desc.environment = sglue_environment()
sg_setup(&desc)
// In frame:
var pass: sg_pass
pass.swapchain = sglue_swapchain()
sg_begin_pass(&pass)
Common Patterns
Game Loop Template
import sokol.app
import sokol.gfx
import sokol.glue
import sokol.gp
var g_time: f32 = 0.0
@c_abi fn init() {
var gfx_desc: sg_desc
gfx_desc.environment = sglue_environment()
sg_setup(&gfx_desc)
var gp_desc: sgp_desc
sgp_setup(&gp_desc)
}
@c_abi fn frame() {
var w = sapp_width()
var h = sapp_height()
g_time = g_time + sapp_frame_duration()
sgp_begin(w, h)
sgp_viewport(0, 0, w, h)
sgp_project(0.0, 800.0, 0.0, 600.0)
// Clear
sgp_set_color(0.0, 0.0, 0.0, 1.0)
sgp_clear()
// Draw game world here...
// Flush to screen
var pass: sg_pass
pass.swapchain = sglue_swapchain()
sg_begin_pass(&pass)
sgp_flush()
sgp_end()
sg_end_pass()
sg_commit()
}
@c_abi fn event(ev: const *sapp_event) {
if (ev.type == EVENTTYPE_KEY_DOWN) {
if (ev.key_code == KEYCODE_ESCAPE) { sapp_quit() }
}
}
@c_abi fn cleanup() {
sgp_shutdown()
sg_shutdown()
}
fn main() {
var desc: sapp_desc
desc.init_cb = init
desc.frame_cb = frame
desc.event_cb = event
desc.cleanup_cb = cleanup
desc.width = 800
desc.height = 600
desc.swap_interval = 1
desc.window_title = "My Game"
sapp_run(&desc)
}
Dynamic Vertex Buffer (Snake Game Pattern)
For games that update geometry every frame (tile maps, particle systems):
var g_verts: f32[5000] // x, y, r, g, b per vertex
var g_vert_count: i32 = 0
var g_vbuf: sg_buffer
@c_abi fn init() {
// ... setup gfx ...
// Create dynamic vertex buffer
var buf_desc: sg_buffer_desc
buf_desc.size = 20000 // 5000 floats * 4 bytes
buf_desc.usage.dynamic_update = true
g_vbuf = sg_make_buffer(&buf_desc)
}
@c_abi fn frame() {
// Rebuild vertex data
g_vert_count = 0
// ... push_vertex() calls to fill g_verts ...
// Upload to GPU
var range: sg_range
range.ptr = &g_verts
range.size = g_vert_count * 20 // 5 floats * 4 bytes per vertex
sg_update_buffer(g_vbuf, &range)
// Draw
sg_apply_pipeline(g_pip)
var bind: sg_bindings
bind.vertex_buffers[0] = g_vbuf
sg_apply_bindings(&bind)
sg_draw(0, g_vert_count, 1)
}
Differences from Raylib
| Feature | Raylib | Sokol |
|---|---|---|
| Text rendering | Built-in (DrawText, LoadFont) | None — use glyph atlas or Clay UI |
| Rounded rectangles | DrawRectangleRounded | Manual triangle fans |
| 3D models | LoadModel, DrawModel | Manual shaders + vertex buffers |
| Audio | Built-in (LoadSound, PlaySound) | Not included (use separate lib) |
| Web export | Limited | Full (compile with Emscripten) |
| GPU backends | OpenGL only | OpenGL, D3D11, Metal, WebGL2, WebGPU |
| Setup complexity | 1 line (InitWindow) | 3-4 structs (sapp_desc, sg_desc, sgp_desc) |
| Performance | Good | Excellent (lower overhead, GPU-native) |
Platform Notes
- Windows: Uses OpenGL Core Profile. Links
opengl32,gdi32,user32,shell32. - Linux: Links
GL,X11,Xi,Xcursor,dl,pthread,m. - macOS: Uses Metal. Links
Cocoa,QuartzCore,Metal,MetalKit. - Web: Compiles to WebGL2/WebGPU via Emscripten.
- TCC: Supported for simple programs. Clang/GCC recommended for optimized builds.
- SOKOL_GLCORE is the default backend on desktop. Change in
sokol_impl.cfor others.
Module Layout
modules/
sokol/
c/
sokol_app.h Platform windowing + events
sokol_gfx.h GPU graphics API
sokol_gp.h 2D drawing painter
sokol_glue.h App ↔ GFX bridge
sokol_impl.c Single compilation unit (SOKOL_IMPL)
gx/
app.gx Window, events, keycodes (build directives here)
gfx.gx Buffers, shaders, pipelines, passes
gp.gx 2D drawing, transforms, blending
glue.gx Environment + swapchain bridge
Examples
| Example | Location | Description |
|---|---|---|
triangle.gx | examples/sokol/ | Low-level rendering: vertex buffer, shader, pipeline |
input.gx | examples/sokol/ | Event handling: keyboard and mouse input |
shapes.gx | examples/sokol/ | 2D drawing: animated shapes with Sokol GP |
snake.gx | examples/sokol/ | Full game: Snake with dynamic vertex buffer |