SDL School
SDL version Which one?

Set up your computer

You can read every lesson and watch every program in this browser. To change the code, you need tools on your own computer.

These lessons expect that you know basic C: variables, functions, and pointers. You need four things:

Step 1: Install the tools

Linux

On Ubuntu or Debian, install the compiler, CMake, and the libraries that SDL needs:

sudo apt install build-essential git cmake pkg-config \
  libasound2-dev libpulse-dev libaudio-dev libfribidi-dev libjack-dev \
  libsndio-dev libx11-dev libxext-dev libxrandr-dev libxcursor-dev \
  libxfixes-dev libxi-dev libxss-dev libxtst-dev libxkbcommon-dev \
  libdrm-dev libgbm-dev libgl1-mesa-dev libgles2-mesa-dev \
  libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev \
  libthai-dev libusb-1.0-0-dev libpipewire-0.3-dev libwayland-dev \
  libdecor-0-dev liburing-dev

Ubuntu 24.04 has no SDL3 package. CMake downloads and builds SDL3 for you in step 3.

On Fedora 43 or later, install the SDL3 package:

sudo dnf install gcc make cmake SDL3-devel

On Arch, install the SDL3 package:

sudo pacman -S gcc make cmake sdl3

On Ubuntu or Debian:

sudo apt install build-essential cmake libsdl2-dev

On Fedora 43 or later:

sudo dnf install gcc make cmake sdl2-compat-devel

On Arch:

sudo pacman -S gcc make cmake sdl2-compat

The sdl2-compat package gives you the SDL2 API, and it runs on SDL3.

macOS

Install the Apple compiler. A window opens, and you follow its steps.

xcode-select --install

If you do not have Homebrew, install Homebrew.

Then install CMake and SDL3:

brew install cmake sdl3

Then install CMake and SDL2:

brew install cmake sdl2-compat

The sdl2-compat package gives you the SDL2 API, and it runs on SDL3.

Windows

Install MSYS2. MSYS2 gives you the compiler and a terminal.

Open the program named MSYS2 UCRT64 from the Start menu. Use this terminal for all the commands on this page.

Install the compiler, CMake, Ninja, and SDL3:

pacman -S mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-ninja mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-sdl3

Install the compiler, CMake, Ninja, and SDL2:

pacman -S mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-ninja mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-SDL2

Step 2: Make the project

Make a folder for the project:

mkdir hello
cd hello

Linux

Save the two files below in the hello folder.

macOS

Save the two files below in the hello folder.

Windows

Save the two files below in the hello folder. In a default MSYS2 installation, the folder is C:\msys64\home\YOURNAME\hello.

main.c
/* Open a window and fill it with one color. */
#define SDL_MAIN_USE_CALLBACKS
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>

static SDL_Window *window = NULL;
static SDL_Renderer *renderer = NULL;

/* SDL calls this once, when the program starts. */
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
    if (!SDL_Init(SDL_INIT_VIDEO)) {
        SDL_Log("SDL_Init failed: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }

    if (!SDL_CreateWindowAndRenderer("Hello, window", 640, 480, 0,
                                     &window, &renderer)) {
        SDL_Log("SDL_CreateWindowAndRenderer failed: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }

    SDL_SetRenderVSync(renderer, 1);
    return SDL_APP_CONTINUE;
}

/* SDL calls this for each event, such as a click on the close button. */
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
{
    if (event->type == SDL_EVENT_QUIT) {
        return SDL_APP_SUCCESS;
    }
    return SDL_APP_CONTINUE;
}

/* SDL calls this again and again, once for each frame. */
SDL_AppResult SDL_AppIterate(void *appstate)
{
    SDL_SetRenderDrawColor(renderer, 32, 96, 160, SDL_ALPHA_OPAQUE);
    SDL_RenderClear(renderer);
    SDL_RenderPresent(renderer);
    return SDL_APP_CONTINUE;
}

/* SDL calls this once, when the program ends. */
void SDL_AppQuit(void *appstate, SDL_AppResult result)
{
    /* SDL destroys the window and the renderer for you. */
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.24)
project(hello C)

# Use an installed SDL 3.4 or later if there is one. If not, download and build it.
include(FetchContent)
FetchContent_Declare(
    SDL3
    GIT_REPOSITORY https://github.com/libsdl-org/SDL.git
    GIT_TAG release-3.4.18
    GIT_SHALLOW TRUE
    FIND_PACKAGE_ARGS 3.4 CONFIG
)
FetchContent_MakeAvailable(SDL3)

add_executable(hello WIN32 main.c)
target_link_libraries(hello PRIVATE SDL3::SDL3)
main.c
/* Open a window and fill it with one color. */
#include <SDL.h>
#include <stdbool.h>

#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif

static SDL_Window *window = NULL;
static SDL_Renderer *renderer = NULL;
static bool running = true;

/* The loop calls this again and again, once for each frame. */
static void frame(void)
{
    SDL_Event event;

    while (SDL_PollEvent(&event)) {
        if (event.type == SDL_QUIT) {
            running = false;
        }
    }

    SDL_SetRenderDrawColor(renderer, 32, 96, 160, SDL_ALPHA_OPAQUE);
    SDL_RenderClear(renderer);
    SDL_RenderPresent(renderer);
}

int main(int argc, char *argv[])
{
    if (SDL_Init(SDL_INIT_VIDEO) != 0) {
        SDL_Log("SDL_Init failed: %s", SDL_GetError());
        return 1;
    }

    if (SDL_CreateWindowAndRenderer(640, 480, 0, &window, &renderer) != 0) {
        SDL_Log("SDL_CreateWindowAndRenderer failed: %s", SDL_GetError());
        SDL_Quit();
        return 1;
    }

    SDL_SetWindowTitle(window, "Hello, window");
    SDL_RenderSetVSync(renderer, 1);

#ifdef __EMSCRIPTEN__
    /* A browser does not let a program stay in its own loop. */
    emscripten_set_main_loop(frame, 0, 1);
#else
    while (running) {
        frame();
    }
#endif

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
    return 0;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(hello C)

find_package(SDL2 REQUIRED CONFIG)

add_executable(hello WIN32 main.c)

# Windows needs SDL2main. The other systems do not have this library.
if(TARGET SDL2::SDL2main)
    target_link_libraries(hello PRIVATE SDL2::SDL2main)
endif()
target_link_libraries(hello PRIVATE SDL2::SDL2)

The lesson Open a window explains each part of main.c.

Step 3: Build and run

Run these commands in the hello folder:

cmake -S . -B build
cmake --build build
./build/hello

The first command prepares the build. The second command compiles the program. The third command runs it.

If your system has no SDL3 package, the first two commands download and build SDL3. This happens one time for each project, and it takes some minutes.

A blue window with the title "Hello, window" opens. Close the window to end the program.

Step 4: Build for the browser

This step is optional. If you want your own programs on a web page, do this step.

Emscripten is a C compiler that makes programs for the browser. Install it in a folder of its own:

git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh

On Windows, use Command Prompt for these commands. Type emsdk.bat for ./emsdk, and type emsdk_env.bat for the last line. You must also install Git for Windows first.

Run the last line of the block again in each new terminal.

Go back to the hello folder, then build and open the page:

emcc main.c -sUSE_SDL=3 -o index.html
emrun index.html
emcc main.c -sUSE_SDL=2 -o index.html
emrun index.html

The first emcc command also builds SDL for the browser, so it takes longer than later builds.