Simple DirectMedia Layer (SDL) is a cross‑platform C library that exposes low‑level access to video, audio, input devices, timers, threads, and more. Version 2 (released 2013) modernised the API, adding hardware‑accelerated 2‑D rendering, Unicode input, high‑DPI support, multiple windows, and mobile platforms. As of the latest stable SDL 2 tag is 2.32.4
.
SDL 3.0 shipped in early 2025 and is now the primary development branch, but SDL 2 remains fully supported and widely deployed. The team provides sdl2‑compat so you can link the same binaries against SDL 3 without code changes .
SDL_image
, SDL_mixer
, SDL_ttf
, SDL_net
).brew install sdl2 sdl2_image sdl2_mixer sdl2_ttf
sudo apt install libsdl2-dev libsdl2-image-dev …
vcpkg install sdl2[sdl2image,sdl2mixer,sdl2ttf]:x64-windows
git clone https://github.com/libsdl-org/SDL.git --branch release-2.32.4
mkdir SDL/build && cd SDL/build
cmake -DSDL_STATIC=ON ..
cmake --build . --config Release --parallel
find_package(SDL2 2.0 REQUIRED)
target_link_libraries(app PRIVATE SDL2::SDL2 SDL2::SDL2main)
Note: On Windows you must ship the SDL2.dll
(or a static build) alongside your executable.
SDL_Init(SDL_INIT_VIDEO); // 1 · boot
SDL_Window* win = SDL_CreateWindow(
"My Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
800, 600, SDL_WINDOW_SHOWN|SDL_WINDOW_RESIZABLE);
SDL_Renderer* renderer = SDL_CreateRenderer(
win, -1, SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC);
bool running = true;
while (running){
SDL_Event e;
while(SDL_PollEvent(&e)){
if(e.type==SDL_QUIT) running = false;
}
SDL_SetRenderDrawColor(renderer, 25,51,102,255);
SDL_RenderClear(renderer);
// render stuff here …
SDL_RenderPresent(renderer);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(win);
SDL_Quit();
The loop above illustrates the SDL “pump event → update → draw → present” pattern.
SDL_INIT_VIDEO
– video, windowing & renderer subsystem.SDL_INIT_AUDIO
– ALSA/CoreAudio/DirectSound backend.SDL_INIT_GAMECONTROLLER
, SDL_INIT_HAPTIC
, SDL_INIT_SENSOR
…|
, e.g. SDL_INIT_VIDEO | SDL_INIT_EVENTS
.You can call SDL_InitSubSystem() later for modules you need on demand, improving startup time for minimal builds (e.g. a dedicated server without audio).
Always pair SDL_Quit() with SDL_Init(). On mobile platforms the OS may reclaim resources aggressively if you leak handles.
SDL_CreateWindow() accepts flags like SDL_WINDOW_FULLSCREEN_DESKTOP
and SDL_WINDOW_ALLOW_HIGHDPI
for Retina/Hi‑DPI support.
A surface lives in CPU RAM (software pixel formats); a texture lives in GPU VRAM. Convert with SDL_CreateTextureFromSurface().
while(SDL_PollEvent(&e)){
switch(e.type){
case SDL_KEYDOWN:
if(e.key.keysym.sym==SDLK_ESCAPE) running=false;
break;
case SDL_CONTROLLERBUTTONDOWN:
// …
}
}
The high‑level SDL_GameController API maps buttons/axes to an Xbox‑style layout, handling hundreds of devices via the built‑in mapping database (you can add your own with SDL_GameControllerAddMapping()).
On mobile/Steam Deck you can read accelerometer and gyroscope data; touch pinch/rotate comes through as SDL_MULTIGESTURE
events.
SDL_AudioSpec want = {0}, have;
want.freq = 48000;
want.format = AUDIO_F32SYS;
want.channels = 2;
want.callback = audio_cb; // pull samples
int dev = SDL_OpenAudioDevice(NULL,0,&want,&have,0);
If want
differs from have
, SDL resamples transparently.
Mix_OpenAudio
() initialises mixing channels.Mix_LoadWAV
() / Mix_PlayChannel
() for SFX.Mix_LoadMUS
() / Mix_PlayMusic
() for streamed music.Use SDL_GetTicks() or SDL_GetPerformanceCounter() for high‑precision deltas. A fixed timestep loop:
const double dt = 1.0/60.0;
double accumulator = 0.0;
uint64_t t0 = SDL_GetPerformanceCounter();
while(running){
uint64_t t1 = SDL_GetPerformanceCounter();
double frame = (t1‑t0)/(double)SDL_GetPerformanceFrequency();
t0 = t1;
accumulator += frame;
while(accumulator >= dt){
update(dt);
accumulator ‑= dt;
}
render(interpolate(accumulator/dt));
}
Create lightweight threads with SDL_CreateThread(); use SDL_LockMutex() or atomic functions like SDL_AtomicIncRef() for synchronisation.
Library | Purpose | Typical formats |
---|---|---|
SDL_image | Bitmap loading → surface/texture | PNG, JPEG, WEBP, AVIF (+stb) |
SDL_mixer | Streaming & sample mixing | WAV, OGG, MP3, FLAC, MOD etc. |
SDL_ttf | TrueType text rasterisation | TTF, OTF, WOFF |
SDL_net | Minimal TCP/UDP wrappers | IPv4 / IPv6 |
Note: On Apple silicon you must build these as universal binaries or ARM‑only to avoid Rosetta overhead.
SDL2main.lib
and define SDL_MAIN_HANDLED
before #include <SDL.h>
if you want to keep your own WinMain
.NSHighResolutionCapable = YES
in Info.plist
for crisp Retina rendering..dylib
inside .app/Frameworks
or use static linking.emcc main.c -o index.html -sUSE_SDL=2 -sUSE_SDL_IMAGE=2 -Os
Pass -sMAX_WEBGL_VERSION=2
for WebGL 2 contexts.
perf
, Instruments
, or Visual Studio Analyzer; enable SDL_DISABLE_SIGNAL_HANDLERS=1
when attaching gdb.-O2 ‑DNDEBUG
and strip symbols; on Windows use /GL /GS‑ /MT
for static CRT builds.<SDL3/SDL.h>
.SDL_PIXELFORMAT_*
→ SDL_PIXELFORMAT_X*
).sdl2‑compat
—relink to benefit from bug fixes immediately.The official migration script using Coccinelle automates 80‑90 % of symbol changes .