Integrating Dear ImGui with SDL3 on WebAssembly (OpenGL ES 3.0 / WebGL 2.0) Интеграция Dear ImGui с SDL3 в WebAssembly (OpenGL ES 3.0 / WebGL 2.0)
A step-by-step guide to compiling Dear ImGui source files with SDL3 and WebGL 2.0 into WebAssembly using Emscripten, CMake, modern SDL3 callbacks, and running it locally in a web browser. Пошаговое руководство по компиляции исходных файлов Dear ImGui с SDL3 и WebGL 2.0 в WebAssembly с помощью Emscripten, CMake и современных callback-функций SDL3 с запуском в веб-браузере.
- MinGW & CMake Setup Guide Руководство по настройке MinGW и CMake
- Emscripten & CMake Setup Guide Руководство по настройке Emscripten и CMake
- Building SDL3 Static Library for WebAssembly (Emscripten & CMake) Сборка статической библиотеки SDL3 для WebAssembly (Emscripten и CMake)
- Installing Dear ImGui Library (OpenGL) for WebAssembly (Emscripten & CMake) Установка библиотеки Dear ImGui (OpenGL) для WebAssembly (Emscripten и CMake)
In web browsers, running a traditional infinite while(running) loop blocks the JavaScript main thread and freezes the page. SDL3 solves this natively with its modern Callbacks Architecture (SDL_AppInit, SDL_AppIterate, SDL_AppEvent, SDL_AppQuit). The browser's requestAnimationFrame automatically drives the application rendering at native display refresh rates without extra boilerplate.
В браузерах традиционный бесконечный цикл while(running) блокирует основной поток JavaScript и замораживает страницу. В SDL3 эта проблема решена нативно с помощью архитектуры функций обратного вызова (SDL_AppInit, SDL_AppIterate, SDL_AppEvent, SDL_AppQuit). Браузерный requestAnimationFrame сам вызывает шаг отрисовки с частотой обновления экрана без дополнительных надстроек.
Step 1: Project Directory Structure Шаг 1: Структура каталогов проекта
Create a project directory named C:\projects\integrate-imgui-sdl3-static-opengl-wasm with the following files and folders:
Создайте директорию проекта с именем C:\projects\integrate-imgui-sdl3-static-opengl-wasm со следующей структурой файлов и папок:
C:\projects\integrate-imgui-sdl3-static-opengl-wasm\
├── CMakeLists.txt
├── config-web.bat
├── build-web.bat
├── public\
│ └── index.html
└── src\
└── main.cpp
Step 2: Create Web Page (public/index.html) Шаг 2: Создание веб-страницы (public/index.html)
Create public/index.html. It provides the HTML5 <canvas> element that SDL3 and WebGL 2.0 render into, disables the right-click context menu, and loads the generated JavaScript wrapper:
Создайте файл public/index.html. Он содержит элемент <canvas>, в который SDL3 и WebGL 2.0 выполняют отрисовку, отключает контекстное меню по правому клику мыши и асинхронно подключает скомпилированный скрипт:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dear ImGui with SDL3 & WebGL 2.0</title>
<style>
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
background-color: #1a1a1a;
}
#canvas {
display: block;
width: 100vw;
height: 100vh;
}
</style>
</head>
<body style="caret-color: transparent;">
<canvas id="canvas"></canvas>
<script>
const canvas = document.getElementById("canvas");
canvas.oncontextmenu = event => { event.preventDefault(); return false; };
</script>
<script async src="./js/app.js"></script>
</body>
</html>
Step 3: Configure CMakeLists.txt Шаг 3: Настройка CMakeLists.txt
Create CMakeLists.txt in the project root directory. We configure Emscripten to enable WebGL 2.0 (-sUSE_WEBGL2=1 -sFULL_ES3=1), link the static libSDL3.a library, compile the Dear ImGui core along with SDL3 and OpenGL3 backends, and output app.js / app.wasm:
Создайте файл CMakeLists.txt в корневой папке проекта. В нём настраивается Emscripten с поддержкой WebGL 2.0 (-sUSE_WEBGL2=1 -sFULL_ES3=1), связывается статическая библиотека libSDL3.a, компилируются исходники Dear ImGui с бэкендами SDL3 и OpenGL3, а результатом сборки являются файлы app.js и app.wasm:
cmake_minimum_required(VERSION 3.21)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release")
endif()
project(integrate-imgui-sdl3-wasm CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# -----------------------------------------------------------------------------
# Paths
# -----------------------------------------------------------------------------
set(SDL3_DIR "C:/libs/sdl3-3.4.18-wasm")
set(IMGUI_DIR "C:/libs/imgui-1.93.0")
# -----------------------------------------------------------------------------
# Executable Target (Emscripten generates .js and .wasm)
# -----------------------------------------------------------------------------
add_executable(app
src/main.cpp
${IMGUI_DIR}/imgui.cpp
${IMGUI_DIR}/imgui_demo.cpp
${IMGUI_DIR}/imgui_draw.cpp
${IMGUI_DIR}/imgui_tables.cpp
${IMGUI_DIR}/imgui_widgets.cpp
${IMGUI_DIR}/backends/imgui_impl_sdl3.cpp
${IMGUI_DIR}/backends/imgui_impl_opengl3.cpp
)
# Set output name to app.js
set_target_properties(app PROPERTIES
OUTPUT_NAME "app"
SUFFIX ".js"
)
# -----------------------------------------------------------------------------
# Include Directories
# -----------------------------------------------------------------------------
target_include_directories(app PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
${SDL3_DIR}/include
${IMGUI_DIR}
${IMGUI_DIR}/backends
)
# -----------------------------------------------------------------------------
# Compile Definitions
# -----------------------------------------------------------------------------
# Enable GLES3 / WebGL2 shaders inside imgui_impl_opengl3
target_compile_definitions(app PRIVATE
IMGUI_IMPL_OPENGL_ES3
)
# -----------------------------------------------------------------------------
# Link SDL3 Static Library
# -----------------------------------------------------------------------------
target_link_libraries(app PRIVATE
${SDL3_DIR}/lib/libSDL3.a
)
# -----------------------------------------------------------------------------
# Emscripten Link Options (WebGL 2.0, Memory Growth, HTML Canvas)
# -----------------------------------------------------------------------------
target_link_options(app PRIVATE
"-sUSE_WEBGL2=1"
"-sFULL_ES3=1"
"-sALLOW_MEMORY_GROWTH=1"
)
Step 4: Create Source Code (src/main.cpp) Шаг 4: Создание исходного кода (src/main.cpp)
Create src/main.cpp using the modern SDL3 callbacks architecture. We define SDL_MAIN_USE_CALLBACKS 1 and implement SDL_AppInit, SDL_AppEvent, SDL_AppIterate, and SDL_AppQuit:
Создайте файл src/main.cpp с использованием современной архитектуры callback-функций SDL3. Мы объявляем макрос SDL_MAIN_USE_CALLBACKS 1 и реализуем SDL_AppInit, SDL_AppEvent, SDL_AppIterate и SDL_AppQuit:
#define SDL_MAIN_USE_CALLBACKS 1
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <GLES3/gl3.h>
#include "imgui.h"
#include "imgui_impl_sdl3.h"
#include "imgui_impl_opengl3.h"
// Application state structure
struct AppState {
SDL_Window* window = nullptr;
SDL_GLContext gl_context = nullptr;
bool show_demo_window = true;
ImVec4 clear_color = ImVec4(0.12f, 0.14f, 0.18f, 1.00f);
int click_counter = 0;
};
// Called once when the application starts
SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[])
{
(void)argc;
(void)argv;
// Initialize SDL3 video subsystem
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD))
{
SDL_Log("Failed to initialize SDL: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
// Allocate application state
AppState* state = new AppState();
*appstate = state;
// Configure WebGL 2.0 / OpenGL ES 3.0 context attributes
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
// Create SDL window mapped to #canvas
SDL_WindowFlags window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY;
state->window = SDL_CreateWindow("Dear ImGui SDL3 WebGL2", 1280, 720, window_flags);
if (!state->window)
{
SDL_Log("Failed to create SDL window: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
// Create WebGL 2.0 context
state->gl_context = SDL_GL_CreateContext(state->window);
if (!state->gl_context)
{
SDL_Log("Failed to create WebGL 2.0 context: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
SDL_GL_MakeCurrent(state->window, state->gl_context);
SDL_GL_SetSwapInterval(1); // Enable V-Sync
// Initialize Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
(void)io;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
// Apply High-DPI display scale
float scale = SDL_GetDisplayContentScale(SDL_GetDisplayForWindow(state->window));
if (scale > 1.0f)
{
ImGui::GetStyle().ScaleAllSizes(scale);
io.FontGlobalScale = scale;
}
// Set dark theme style
ImGui::StyleColorsDark();
// Initialize platform and WebGL2 renderers
ImGui_ImplSDL3_InitForOpenGL(state->window, state->gl_context);
const char* glsl_version = "#version 300 es";
ImGui_ImplOpenGL3_Init(glsl_version);
SDL_Log("Dear ImGui WebAssembly initialized successfully");
return SDL_APP_CONTINUE;
}
// Called for every incoming SDL event
SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event)
{
(void)appstate;
ImGui_ImplSDL3_ProcessEvent(event);
if (event->type == SDL_EVENT_QUIT)
{
return SDL_APP_SUCCESS;
}
return SDL_APP_CONTINUE;
}
// Called once per frame to update and render
SDL_AppResult SDL_AppIterate(void* appstate)
{
AppState* state = (AppState*)appstate;
ImGuiIO& io = ImGui::GetIO();
// Start Dear ImGui frame
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplSDL3_NewFrame();
ImGui::NewFrame();
// 1. Show the Dear ImGui demo window
if (state->show_demo_window)
{
ImGui::ShowDemoWindow(&state->show_demo_window);
}
// 2. Custom WebAssembly window
{
ImGui::Begin("Dear ImGui with SDL3 & WebGL 2.0 (Wasm)");
ImGui::Text("Application average: %.3f ms/frame (%.1f FPS)",
1000.0f / io.Framerate, io.Framerate);
ImGui::Separator();
ImGui::Checkbox("Show ImGui Demo Window", &state->show_demo_window);
if (ImGui::Button("Click Me!", ImVec2(140, 40)))
{
state->click_counter++;
}
ImGui::SameLine();
ImGui::Text("Clicks: %d", state->click_counter);
ImGui::Separator();
ImGui::ColorEdit3("Background Color", (float*)&state->clear_color);
ImGui::End();
}
// Rendering
ImGui::Render();
int display_w, display_h;
SDL_GetWindowSizeInPixels(state->window, &display_w, &display_h);
glViewport(0, 0, display_w, display_h);
glClearColor(state->clear_color.x * state->clear_color.w,
state->clear_color.y * state->clear_color.w,
state->clear_color.z * state->clear_color.w,
state->clear_color.w);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
SDL_GL_SwapWindow(state->window);
return SDL_APP_CONTINUE;
}
// Called once when the application is shutting down
void SDL_AppQuit(void* appstate, SDL_AppResult result)
{
(void)result;
AppState* state = (AppState*)appstate;
if (state)
{
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplSDL3_Shutdown();
ImGui::DestroyContext();
if (state->gl_context)
{
SDL_GL_DestroyContext(state->gl_context);
}
if (state->window)
{
SDL_DestroyWindow(state->window);
}
delete state;
}
SDL_Quit();
}
Step 5: Create Configuration and Build Scripts Шаг 5: Создание скриптов конфигурации и сборки
Create two helper batch scripts in the project root to automate CMake configuration with Emscripten and file copying: Создайте два вспомогательных bat-файла в корне проекта для автоматизации конфигурации CMake через Emscripten и копирования скомпилированных файлов:
config-web.bat
emcmake cmake -S . -B dist/web
build-web.bat
This script compiles the project in parallel, ensures the output folder public\js exists, and copies the generated app.js and app.wasm binaries automatically:
Этот скрипт компилирует проект параллельно, проверяет наличие папки public\js и автоматически копирует собранные файлы app.js и app.wasm:
@echo off
pushd "%~dp0dist\web"
cmake --build . --parallel
popd
if not exist "%~dp0public\js" mkdir "%~dp0public\js"
copy /y "%~dp0dist\web\app.wasm" "%~dp0public\js\"
copy /y "%~dp0dist\web\app.js" "%~dp0public\js\"
Step 6: Configure and Build Шаг 6: Конфигурация и сборка
Open Command Prompt (CMD) in your project root with the Emscripten environment active, then run: Откройте командную строку (CMD) в корне проекта с активированным окружением Emscripten и выполните:
config-web.bat
build-web.bat
After a successful build, app.js and app.wasm will be placed in the public/js/ directory.
После успешной сборки файлы app.js и app.wasm будут скопированы в директорию public/js/.
Step 7: Run a Local HTTP Server Шаг 7: Запуск локального HTTP-сервера
Web browsers do not allow loading WebAssembly (.wasm) directly via the file:// protocol due to CORS security policies. You must serve the public directory using a local HTTP server:
Веб-браузеры блокируют загрузку WebAssembly (.wasm) через протокол file:// из соображений безопасности (CORS). Папку public необходимо раздавать через локальный HTTP-сервер:
Look at the URL printed in your console (typically http://localhost:8080 or http://localhost:8080 / 8081) and open it in your browser:
Посмотрите адрес, выведенный в консоли (обычно http://localhost:8080 или http://localhost:8080 / 8081), и откройте его в браузере:
404 Not Found for favicon.ico in the console is normal and harmless — browsers simply request a tab icon automatically.
Примечание: Ошибка 404 Not Found для favicon.ico в консоли абсолютно нормальна — браузер просто автоматически запрашивает иконку вкладки.
You will see Dear ImGui rendering smoothly inside your web browser powered by WebAssembly, SDL3, and WebGL 2.0! Вы увидите работающий интерфейс Dear ImGui прямо в веб-браузере благодаря WebAssembly, SDL3 и WebGL 2.0!
Complete source code for this project is available on GitHub: Полный исходный код проекта доступен на GitHub:
View on GitHub Смотреть на GitHubSupport My Work Поддержать проект
If these tutorials helped you, consider buying me a coffee! Если эти туториалы вам помогли, вы можете поддержать автора.
Sberbank (Russia only) Сбербанк (только для РФ)
Direct transfer via phone number (Russia only) Перевод по номеру телефона (только для РФ)
USDT TRC20
Support via Cryptocurrency Поддержка криптовалютой