Real-time 1-second rolling window, independent of tick rate or display refresh.
skele
skele is a lightweight, generic game framework written in C99. It provides a platform-agnostic core (clock, video, input) split cleanly from OS/backend-specific implementations, so games and engines built on it stay portable without fighting the framework's opinions.
It pairs naturally with stk for hot-reloadable game modules, but has no hard dependency on it beyond the optional skele_stk_setup/skele_stk_teardown glue.
Key Features
- Platform-agnostic core (
include/) with OS/backend-specific implementations swapped in at build time (src/platform/) - Two video backends behind one API: software/SDL3 blit (
libskele) and OpenGL (libglskele) - Headless server builds, linking only the engine core, no client/video/input code pulled in
- Fixed-timestep clock with monotonic time and signal-aware sleep (POSIX and Win32)
- SDL3-backed input: keyboard, gamepad (up to 4 pads), mouse, with held/pressed-this-frame state and analog axes
- 256-color palette + software blit path for indexed-color renderers (Doom-era style pipelines)
- Optional
stkhot-reload integration viaskele_stk_setup/skele_stk_teardown
Quick Start
Building
# Unix (Linux/BSD/macOS)
./build.sh debug release
# Windows
build.bat debug release
Builds three static libraries into bin/{debug,release}:
libskele.a- software/SDL3 client backendlibglskele.a- OpenGL client backendlibskeleserver.a- headless engine core only
Raspberry Pi 5 is auto-detected and builds against GL 3.1 instead of the default 3.3.
Installation
Unix (Linux/BSD/macOS)
./build.sh install
Installs to /usr on Linux, /usr/local on BSD/macOS by default. Use PREFIX to customize:
./build.sh PREFIX=$HOME/.local install
Windows
build.bat release
- Once finished building, copy the headers from
include/toyour_project/include/skele/and copy the built.lib/.dllfiles to your project'slibdirectory.
Usage
Basic Example (software client)
#include "client/blit.h"
#include "client/input.h"
#include "client/video.h"
#include "clock.h"
#include "skele.h"
#include <stdlib.h>
#include <stk/stk.h>
static uint8_t running = 1;
static void on_signal(void) { running = 0; }
int main(void)
{
skele_video_config_t video_cfg;
uint64_t last, now, elapsed, accum = 0;
uint8_t *pixels;
skele_clock_init(on_signal);
if (skele_stk_setup() != SKELE_INIT_SUCCESS)
return 1;
if (skele_init() != SKELE_INIT_SUCCESS) {
skele_stk_teardown();
return 1;
}
video_cfg.render_width = SKELE_DEFAULT_RENDER_WIDTH;
video_cfg.render_height = SKELE_DEFAULT_RENDER_HEIGHT;
video_cfg.window_width = 0;
video_cfg.window_height = 0;
video_cfg.flags = 0;
if (skele_video_init(video_cfg) != SKELE_INIT_SUCCESS) {
skele_stk_teardown();
skele_shutdown();
return 1;
}
pixels = calloc(video_cfg.render_width * video_cfg.render_height,
sizeof(uint8_t));
last = skele_time_ns();
while (running) {
stk_poll();
if (!skele_input_poll())
break;
now = skele_time_ns();
elapsed = now - last;
accum += elapsed;
last = now;
while (accum >= skele_tick_ns) {
skele_tick();
accum -= skele_tick_ns;
}
skele_video_blit(pixels);
skele_video_present();
}
free(pixels);
skele_video_shutdown();
skele_stk_teardown();
skele_shutdown();
return 0;
}
See example/client, example/gl_client, and example/server for the full reference loops, including a fixed-timestep accumulator suitable for interpolated rendering.
Headless / Server Builds
Link libskeleserver.a instead of libskele.a/libglskele.a to get the engine core (clock, tick, stk glue) without pulling in any video/input/SDL3 dependency:
cc -o my_server main.c -lskeleserver -lstk
API Reference
Core (skele.h)
uint8_t skele_init(void)/void skele_shutdown(void)- engine lifecyclevoid skele_tick(void)- advance one simulation tickvoid skele_set_tick_rate(uint8_t rate)- configure ticks/sec (defaultSKELE_DEFAULT_TICK_RATE)uint64_t skele_tick_ns- nanoseconds per tick, for accumulator loopsuint8_t skele_stk_setup(void)/void skele_stk_teardown(void)- wire upstkhot-reload
Clock (clock.h)
void skele_clock_init(void (*on_signal)(void))- monotonic clock + signal handlinguint64_t skele_time_ns(void)- current monotonic timevoid skele_sleep_ns(uint64_t ns)- sleep for a duration
Video (client/video.h)
uint8_t skele_video_init(skele_video_config_t cfg)/void skele_video_shutdown(void)void skele_video_present(void)- present the current framevoid skele_video_set_title(const char *title)void skele_video_toggle_fullscreen(void)/skele_video_set_fullscreen_kind(...)void skele_video_cycle_scale(void)- cycle integer window scalevoid skele_video_set_mouse_grab(uint8_t grab)
Blit / Palette (client/blit.h, client/palette.h)
void skele_video_blit(uint8_t *pixels)- blit an indexed-color framebuffer through the active palettevoid skele_palette_set(skele_palette_t pal)/skele_palette_set_index(uint8_t index, uint32_t color)
Input (client/input.h)
uint8_t skele_input_poll(void)- pump platform events, returns0on quituint8_t skele_key_down(skele_key_t key)/skele_key_held(skele_key_t key)- pressed-this-frame vs. currently-helduint8_t skele_pad_connected(uint8_t pad),skele_pad_button_down/held(...),float skele_pad_axis(...)- up toSKELE_MAX_PADSgamepadsvoid skele_mouse_delta(int32_t *dx, int32_t *dy),skele_mouse_button_down/held(...)
Project Status
Current Version: 0.0.0
Early/active development. Platform backends (clock, video, input) are functional and exercised by real client applications, but the engine core (skele_init/skele_tick) is still minimal. API is not yet stable; expect breaking changes until the core lifecycle is fleshed out.
No test suite yet.
License
Mozilla Public License 2.0 (MPL-2.0)
Contributing
Contributions welcome! Please ensure code follows C99 standard and works across all supported platforms.