A new version: Deploying SDL3 app on Android using SDL3-x.y.z.aar
New version for SDL 3.3: Setting Up SDL 3.3 for Android Development
Result of testing: Discord
Testing of the GitHub - madebr/sdlcross: Little SDL application buildable on Desktop + Android example with SDL3 Renderer API that shows squares when we touch the screen:

Example in pure OpenGL ES 2.0 that draws a simple green triangle:
I used the https://github.com/Genymobile/scrcpy app to create a screenshot on PC using USB cable from the phone.
Added the following info to the guide: Setting Up SDL 3.3 for Android Development
- Important! The project name in
CMakeLists.txt:
# Declare the project
project(my-project)
- Must be the same as the project name in the
.javafile located atapp\src\main\java\com\example\sdlcross, inside thegetLibraries()method:
package com.example.sdlcross;
import org.libsdl.app.SDLActivity;
public class MyActivity extends SDLActivity {
protected String[] getLibraries() {
return new String[] { "my-project" };
}
}
There is my mistake above. The executable name should be the same with the return new String[] { "sdlcross" };:
- Important! The executable name in
CMakeLists.txt:
add_executable(sdlcross src/main.c)
- Must be the same as the project name in the
.javafile located atapp\src\main\java\com\example\sdlcross, inside thegetLibraries()method:
package com.example.sdlcross;
import org.libsdl.app.SDLActivity;
public class MyActivity extends SDLActivity {
protected String[] getLibraries() {
return new String[] { "sdlcross" };
}
}
Note. The SDL 3.3 is not release yet. I call the currect master branch like 3.3.
Drawing a triangle using SDL3 (OpenGL, WebAssembly)
Guide: Setting up SDL3 with OpenGL ES 3.0 for WebAssembly
Note. You can disable multisampling if you delete this code:
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); // Enable MULTISAMPLE
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 2); // can be 2, 4, 8 or 16
Note 2. There is no difference between 2 and 4 (value of multisampling) at least in this triangle.
Drawing a rotated rectangle using cglm (OpenGL, WebAssembly)
Guide: Setting up cglm with SDL3 and OpenGL ES 3.0 for WebAssembly
Size of the Release build - 942 KB:
Drawing a texture using SDL3_image (OpenGL, WebAssembly)
Guide: Setting up SDL3_image with OpenGL ES 3.0 (Wasm)
Drawing a texture using stb_image (OpenGL, WebAssembly)
Rendering a text using SDL3_ttf (OpenGL, WebAssembly)
Guide: Setting up SDL3_ttf (OpenGL, WebAssembly)
Size of the Release build - 1.81 MB:
Asset: LiberationSans-Regular.ttf file - 401 KB (link)
[Workaround] Background music looping in Wasm (SDL3_mixer)
I’ve been running into an issue where MIX_SetTrackLoops(..., -1) doesn’t loop background music infinitely in my Wasm/Emscripten build:
The Issue: The track stops after it completes its first run, and the loop command is ignored:
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
{
App *myApp = (App *)appstate;
// Handle initial music trigger on any input
bool isInput = (event->type == SDL_EVENT_KEY_DOWN ||
event->type == SDL_EVENT_MOUSE_BUTTON_DOWN);
if (!myApp->musicStarted && isInput)
{
MIX_SetTrackLoops(myApp->musicTrack, -1); // -1 loops infinitely, 0 disables looping
MIX_PlayTrack(myApp->musicTrack, 0);
myApp->musicStarted = true;
}
The Workaround: You must create an explicit options packet using SDL_CreateProperties(), assign your infinite loop instruction to MIX_PROP_PLAY_LOOPS_NUMBER, and feed that packet into MIX_PlayTrack:
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
{
App *myApp = (App *)appstate;
// Handle initial music trigger on any input
bool isInput = (event->type == SDL_EVENT_KEY_DOWN ||
event->type == SDL_EVENT_MOUSE_BUTTON_DOWN);
if (!myApp->musicStarted && isInput)
{
// Create an options configuration packet
SDL_PropertiesID props = SDL_CreateProperties();
// Pack the "infinite loop" instruction (-1) into the container
SDL_SetNumberProperty(props, MIX_PROP_PLAY_LOOPS_NUMBER, -1);
// Hand the configuration properties over to SDL3_mixer
MIX_PlayTrack(myApp->musicTrack, props);
// Clean up the handle container (SDL copies properties internally)
SDL_DestroyProperties(props);
myApp->musicStarted = true;
}
// Handle rest of events...
return SDL_APP_CONTINUE;
}
Playing a background music and sound effect using SDL3_mixer (OpenGL, WebAssembly)
Guide: Setting up SDL3_mixer (OpenGL, WebAssembly)
Release build size:
C and C++ Release builds has the same sizes - 4.18 MB:
Asset sizes:
- Winds Of Stories.ogg - 2.43 MB
- Picked Coin Echo 2.wav - 296 KB (after
Audacityconversion)
Playing a background music and sound effect using OpenAL (OpenGL, WebAssembly)
Guide: Setting up OpenAL (OpenGL, WebAssembly)
Source code: GitHub Repository
Release build size - 3.84 MB:
Asset sizes:
- Winds Of Stories.ogg - 2.43 MB
- Picked Coin Echo 2.wav - 296 KB (after
Audacityconversion)
Read comments:
if (EMSCRIPTEN)
target_link_options(app PRIVATE
"-Oz" # Optimize for smallest code size (aggressively strips metadata and optimizes for binary footprint).
"-sMALLOC=emmalloc" # Use 'emmalloc', a smaller, simpler memory allocator suitable for memory-constrained web apps.
"-sUSE_SDL=0" # Disable the legacy Emscripten-provided SDL1/SDL2 port (allows using custom or system-integrated SDL3).
"-sMAX_WEBGL_VERSION=2" # Limit the maximum WebGL context version to 2.0.
"-sMIN_WEBGL_VERSION=2" # Set the minimum required WebGL version to 2.0 (disallows fallback to 1.0).
"-sFULL_ES3=1" # Enable full WebGL 2.0 / OpenGL ES 3.0 feature set support.
)
endif()
Short comments:
if (EMSCRIPTEN)
target_link_options(app PRIVATE
"-Oz" # Maximize code size optimization
"-sMALLOC=emmalloc" # Use minimal memory allocator
"-sUSE_SDL=0" # Disable built-in SDL
"-sMAX_WEBGL_VERSION=2" # Set WebGL 2.0 max
"-sMIN_WEBGL_VERSION=2" # Require WebGL 2.0 min
"-sFULL_ES3=1" # Enable full GLES3/WebGL 2
)
endif()
Failed to enable VSync on Intel(R) HD Graphics 630 (i7-7700)
I cannot enable it on Desktop:
int status = SDL_GL_SetSwapInterval(1);
if (status != 0)
{
SDL_Log("VSync failed: %s", SDL_GetError());
}
Solution is to limit FPS:
SDL_AppResult SDL_AppIterate(void *appState)
{
App *app = (App *)appState;
// High-precision timing variables
static Uint64 lastFrameTime = 0;
const Uint64 targetFrameTimeNS = 16666666; // ~16.6ms for 60 FPS
Uint64 now = SDL_GetTicksNS();
// If we haven't hit the target time, yield the CPU
if ((now - lastFrameTime) < targetFrameTimeNS)
{
// Sleep to yield control back to the Windows scheduler
// This drops CPU usage from 15% to <1%
SDL_Delay(1);
return SDL_APP_CONTINUE;
}
// Update our last frame time
lastFrameTime = now;
glClear(GL_COLOR_BUFFER_BIT);
Before:
After:
P.S. I use the latest Intel drivers:
The thread is about building SDL3 ‘for Android and WebAssembly’ so when you say “on desktop” do you mean specifically when running in a browser, or more generally?
I have moved this problem to the topic: Failed to enable VSync on Intel(R) HD Graphics 630 (i7-7700)














