PHPackages                             microscrap/sdl3 - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. microscrap/sdl3

ActiveLibrary[Framework](/categories/framework)

microscrap/sdl3
===============

LibSDL3 Bindings for The PHP SDL3 Extension

0.5.0.x-dev(4d ago)001MITPHP ^8.3

Since Jul 7Compare

[ Source](https://github.com/microscrap/sdl3)[ Packagist](https://packagist.org/packages/microscrap/sdl3)[ Docs](https://dosr.projectsaturnstudios.com)[ RSS](/packages/microscrap-sdl3/feed)WikiDiscussions Synced today

READMEChangelogDependencies (1)Versions (2)Used By (1)

microscrap/sdl3 — LibSDL3 bindings for PHP
==========================================

[](#microscrapsdl3--libsdl3-bindings-for-php)

PHP library that wraps the [**sdl3**](https://github.com/php-io-extensions/sdl3) extension with global helpers, enums, and data objects. Every helper delegates to a static wrapper class under `Microscrap\Bindings\SDL3`.

The package covers the entire extension surface (636+ methods across 22 extension classes): init, error, timers, properties, surfaces, 2D rendering, video/windows, OpenGL, events, keyboard, mouse, display, clipboard, joystick, gamepad, audio, file dialogs, and the full SDL GPU API.

Highlights
----------

[](#highlights)

- Two calling styles — exact C names (`SDL_CreateWindow(...)`) or static wrapper classes (`Video::createWindow(...)`)
- Every opaque SDL handle wrapped in a typed `final readonly` data object (`SDLWindow`, `SDLRenderer`, `SDLGPUDevice`, …)
- Full enum layer transcribed from the SDL 3.4.x headers — 67 int/string-backed enums including `Scancode` (~290 cases), `PixelFormat`, `EventType`, and the GPU format family
- C-style error handling: no exceptions in `src/`, failed creations return `null`, failed operations return `false`; details via `SDL_GetError()`
- Headless-friendly: software renderer, dummy video driver, virtual joysticks, driverless audio streams all work without a display
- Coverage drift guard: a Pest test reflects the extension and fails if any extension method lacks a wrapper method or helper function

Requirements
------------

[](#requirements)

- PHP 8.3+
- **ext-sdl3** ^0.5.0 — install from [php-io-extensions/sdl3](https://github.com/php-io-extensions/sdl3)

Installation
------------

[](#installation)

Confirm **ext-sdl3** is loaded:

```
php -m | grep sdl3
```

```
composer require microscrap/sdl3
```

Composer autoloads all helper files in `src/Helpers/`, registering the global `SDL_*` functions when the package is installed. Helpers are only defined if the name is not already taken (`function_exists` guard).

The two calling styles
----------------------

[](#the-two-calling-styles)

**C-ish** — global functions with exact SDL C names:

```
SDL_Init(InitFlag::SDL_INIT_VIDEO->value);
$window = SDL_CreateWindow('hello', 640, 480, WindowFlag::SDL_WINDOW_HIDDEN);
SDL_DestroyWindow($window);
SDL_Quit();
```

**OO-ish** — static wrapper classes, same behavior:

```
use Microscrap\Bindings\SDL3\Init;
use Microscrap\Bindings\SDL3\Video;

Init::init(InitFlag::SDL_INIT_VIDEO);
$window = Video::createWindow('hello', 640, 480, WindowFlag::SDL_WINDOW_HIDDEN);
Video::destroyWindow($window);
Init::quit();
```

Helpers never touch the extension directly; they delegate one-to-one to the wrapper classes, which are the only layer calling `Sdl3\SDL\*`. Both styles accept and return the same data objects, and every flag/enum parameter takes `EnumType|int`.

### Name transforms

[](#name-transforms)

- Wrapper methods drop the `SDL` prefix: `SDLCreateWindow` → `Video::createWindow()`.
- The `GL` class also drops the redundant GL token (`SDLGLCreateContext` → `GL::createContext()`); EGL methods keep a lowercase `egl` prefix.
- The `GPU` class drops the redundant GPU token (`SDLCreateGPUTexture` → `GPU::createTexture()`).
- Helpers use the exact SDL C name, including the odd ones: `SDL_rand()`, `SDL_GL_CreateContext()`, `SDL_GDKSuspendGPU()`. The extension-only transfer-buffer conveniences keep their extension names (`writeToGPUTransferBuffer()`, `readFromGPUTransferBuffer()`).

Wrapper classes
---------------

[](#wrapper-classes)

ClassWrapsMethodsSubsystem`Init``Sdl3\SDL\SDL`22init/quit, version, platform, app metadata`Error``SDLError`4get/set/clear error`Timer``Timer\SDLTimer`2ticks, delay`Properties``SDLProperties`20property groups`Surface``Surface\SDLSurface`72surfaces, palettes, pixel formats`Render``Render\SDLRender`94renderers, textures`Video``Video\SDLVideo`83windows, video drivers`GL``Video\SDLGL`20OpenGL + EGL`Events``Events\SDLEvents` + 4 more25event queue, watches, quit, drops`Keyboard``Events\SDLKeyboard`28keyboard state, keycodes`Mouse``Events\SDLMouse`27mouse state, cursors`Display``Events\SDLDisplayEvents`13displays, modes`Clipboard``Events\SDLClipboardEvents`10clipboard text/data`Joystick``Input\SDLJoystick`58joysticks incl. virtual`Gamepad``Input\SDLGamepad`72gamepads, mappings, sensors`Audio``Audio\SDLAudio`57devices, streams, WAV`Dialog``Dialog\SDLDialog`4native file dialogs`GPU``Gpu\SDLGPU`105full SDL GPU API + render statesData objects live under `Microscrap\Bindings\SDL3\DataObjects` (27 classes; each holds the raw handle as `->ptr`, or `->id` for instance IDs). Enums live under `Microscrap\Bindings\SDL3\Enums` (67 enums; case names match the C macros exactly).

Examples
--------

[](#examples)

### Software rendering (fully headless)

[](#software-rendering-fully-headless)

```
use Microscrap\Bindings\SDL3\Enums\PixelFormat;
use Sdl3\SDL\Surface\SDLSurface;

SDL_Init(0);

$surface = SDLSurface::SDLCreateSurface(64, 64, PixelFormat::SDL_PIXELFORMAT_RGBA8888->value);
$renderer = SDL_CreateSoftwareRenderer((int) $surface['ptr']);

SDL_SetRenderDrawColor($renderer, 255, 0, 0, 255);
SDL_RenderClear($renderer);
SDL_RenderFillRect($renderer, ['x' => 8, 'y' => 8, 'w' => 48, 'h' => 48]);
SDL_RenderPresent($renderer);

$pixels = SDL_RenderReadPixels($renderer);

SDL_DestroyRenderer($renderer);
SDLSurface::SDLDestroySurface((int) $surface['ptr']);
SDL_Quit();
```

### Window + events (dummy driver works headless)

[](#window--events-dummy-driver-works-headless)

```
use Microscrap\Bindings\SDL3\Enums\EventType;
use Microscrap\Bindings\SDL3\Enums\InitFlag;
use Microscrap\Bindings\SDL3\Enums\WindowFlag;

putenv('SDL_VIDEODRIVER=dummy');
SDL_Init(InitFlag::SDL_INIT_VIDEO->value);

$window = SDL_CreateWindow('demo', 320, 240, WindowFlag::SDL_WINDOW_HIDDEN);

while (! is_null($event = SDL_PollEvent())) { // ?SDLEventRef
    if ($event->eventType === EventType::SDL_EVENT_QUIT->value) {
        break;
    }
}

SDL_DestroyWindow($window);
SDL_Quit();
```

### Virtual joystick (no hardware needed)

[](#virtual-joystick-no-hardware-needed)

```
use Microscrap\Bindings\SDL3\Enums\InitFlag;
use Microscrap\Bindings\SDL3\Enums\JoystickType;

SDL_Init(InitFlag::SDL_INIT_JOYSTICK->value);

$id = SDL_AttachVirtualJoystick([
    'type' => JoystickType::SDL_JOYSTICK_TYPE_GAMEPAD->value,
    'naxes' => 2,
    'nbuttons' => 4,
]);
$joystick = SDL_OpenJoystick($id);

SDL_SetJoystickVirtualAxis($joystick, 0, 12345);
SDL_UpdateJoysticks();
$value = SDL_GetJoystickAxis($joystick, 0); // 12345

SDL_CloseJoystick($joystick);
SDL_DetachVirtualJoystick($id);
SDL_Quit();
```

### Audio stream round-trip (driverless)

[](#audio-stream-round-trip-driverless)

```
use Microscrap\Bindings\SDL3\Enums\AudioFormat;

SDL_Init(0);

$spec = ['format' => AudioFormat::SDL_AUDIO_S16LE->value, 'channels' => 2, 'freq' => 44100];
$stream = SDL_CreateAudioStream($spec, $spec);

SDL_PutAudioStreamData($stream, $pcmBytes);
SDL_FlushAudioStream($stream);
$out = SDL_GetAudioStreamData($stream, strlen($pcmBytes));

SDL_DestroyAudioStream($stream);
SDL_Quit();
```

### GPU buffer round-trip (Metal/Vulkan/D3D12)

[](#gpu-buffer-round-trip-metalvulkand3d12)

```
use Microscrap\Bindings\SDL3\Enums\GPUBufferUsage;
use Microscrap\Bindings\SDL3\Enums\GPUShaderFormat;
use Microscrap\Bindings\SDL3\Enums\GPUTransferBufferUsage;
use Microscrap\Bindings\SDL3\Enums\InitFlag;
use Microscrap\Bindings\SDL3\GPU;

SDL_Init(InitFlag::SDL_INIT_VIDEO->value); // GPU devices need the video subsystem

$device = GPU::createDevice(GPUShaderFormat::SDL_GPU_SHADERFORMAT_MSL->value | GPUShaderFormat::SDL_GPU_SHADERFORMAT_SPIRV->value);

$buffer = GPU::createBuffer($device, ['usage' => GPUBufferUsage::SDL_GPU_BUFFERUSAGE_VERTEX->value, 'size' => 256]);
$upload = GPU::createTransferBuffer($device, ['usage' => GPUTransferBufferUsage::SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD->value, 'size' => 256]);

GPU::writeToTransferBuffer($device, $upload, $payload);

$cb = GPU::acquireCommandBuffer($device);
$copyPass = GPU::beginCopyPass($cb);
GPU::uploadToBuffer($copyPass, ['transfer_buffer' => $upload->ptr, 'offset' => 0], ['buffer' => $buffer->ptr, 'offset' => 0, 'size' => 256]);
GPU::endCopyPass($copyPass);

$fence = GPU::submitCommandBufferAndAcquireFence($cb);
GPU::waitForFences($device, true, [$fence]);
GPU::releaseFence($device, $fence);

GPU::releaseTransferBuffer($device, $upload);
GPU::releaseBuffer($device, $buffer);
GPU::destroyDevice($device);
SDL_Quit();
```

Struct parameters (rects, audio specs, GPU create-infos, pass targets) are associative arrays mirroring the C struct field names exactly; each wrapper method documents the expected keys in its docblock.

Error handling
--------------

[](#error-handling)

Nothing in `src/` throws. The package keeps SDL's C conventions:

- creation functions return `null` on failure (`?SDLWindow`, `?SDLGPUDevice`, …)
- operations return `false` (or `-1`) on failure
- call `SDL_GetError()` for the reason

Note: the underlying extension itself throws `RuntimeException` from a handful of calls (e.g. `SDL_CreateGPUDevice` with no usable driver, GDK functions off-Xbox). Those propagate as-is.

Testing
-------

[](#testing)

```
./vendor/bin/pest
```

- `tests/Unit` runs without the extension: coverage drift guard (against a committed 0.5.0 method snapshot), style audit (no class constants, no throws, guarded helpers, backed enums, uppercase cases).
- `tests/Feature` is gated on `extension_loaded('sdl3')` and runs headless: dummy video driver, software renderer pixel round-trips, virtual joysticks, driverless audio streams, and a real GPU buffer round-trip when a device is available (skips gracefully otherwise).

License
-------

[](#license)

MIT. See [LICENSE](LICENSE).

###  Health Score

35

—

LowBetter than 77% of packages

Maintenance99

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity34

Early-stage or recently created project

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

4d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/10563160?v=4)[Angel Gonzalez](/maintainers/projectsaturnstudios)[@projectsaturnstudios](https://github.com/projectsaturnstudios)

---

Tags

frameworkscrapyard-io

###  Code Quality

TestsPest

### Embed Badge

![Health badge](/badges/microscrap-sdl3/health.svg)

```
[![Health](https://phpackages.com/badges/microscrap-sdl3/health.svg)](https://phpackages.com/packages/microscrap-sdl3)
```

###  Alternatives

[pestphp/pest-plugin-stressless

Stressless plugin for Pest

681.0M18](/packages/pestphp-pest-plugin-stressless)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
