Introduction
raylib-rs is a safe, idiomatic Rust binding to raylib 6.0 — the simple and easy-to-use game programming library. It lets you write 2D/3D games, simulations, and visualizations in Rust with the same low-ceremony feel as the original C library, without requiring unsafe in user code.
Who this is for
This book is for Rust developers who want to build interactive graphical applications — games, simulations, visualizations, or tools — using raylib’s clean API. You should be comfortable with basic Rust (ownership, traits, closures), but you do not need prior game development experience. No C/C++ knowledge is required.
What’s in scope
raylib-rs 6.0 covers the full raylib 6.0 surface:
- Window management and drawing — 2D and 3D rendering, shapes, textures, render targets.
- Input — keyboard, mouse, gamepad, touch.
- Text and fonts — system fonts, custom fonts, SDF rendering.
- 3D models — meshes, materials, skeletal animation via
ModelAnimations. - Audio — sounds, music streams,
AudioStreamfor custom mixing. - raymath — vectors, matrices, quaternions, easing functions. Math methods come via a C shim so there is no extra dependency by default.
- raygui — immediate-mode GUI panels, buttons, sliders, text boxes.
- rlgl — low-level OpenGL abstraction for custom rendering.
- Software renderer — a headless
PLATFORM=Memorybackend for pixel-probe testing without a GPU or window.
What’s out of scope here
- The C raylib reference — see raylib.com for the upstream API docs.
- The rustdoc API reference — generated from doc comments; published to docs.rs with each crate release.
- The showcase examples — full ports of the raylib C example set, shipping as a GitHub Pages site in WS9.
How to read this book
Getting Started — set up the dependency and open your first window in under 30 lines. Then follow the install guide for your platform (Windows, macOS, Linux, or Web/Wasm).
Core Concepts — the conventions that show up everywhere: the RaylibHandle/RaylibThread ownership model; RAII resources and how they are cleaned up; strings and allocations; safety rules; feature flags and platform support.
Modules — per-area narrative chapters: window and drawing, input, shapes, textures, text, 3D, audio, raymath, collision, raygui, rlgl, the software renderer, callbacks, and error handling.
Ecosystem — opt-in integrations: glam and mint for interop with other math libraries, serde for serialization, and pointers to what comes next.
Quickstart
This chapter gets you from zero to a working window in under 30 lines of Rust. If you have not installed the native build dependencies yet, see the install guide for your platform first.
Add the dependency
In your Cargo.toml:
[dependencies]
raylib = "6.0.0-rc.2"
Note:
6.0.0-rc.2is the published release-candidate of the 6.0 line on crates.io while the canonical merge gets additional review. The headings under this book chapter describe what is shipping in the eventual6.0.0final release; cargo will not pick up a pre-release tag fromraylib = "6.0"alone, so the exact6.0.0-rc.2pin is intentional.
Open a window
extern crate raylib;
use raylib::prelude::*;
fn main() {
let (mut rl, thread) = raylib::init()
.size(640, 480)
.title("Hello, raylib-rs")
.build();
while !rl.window_should_close() {
let mut d = rl.begin_drawing(&thread);
d.clear_background(Color::WHITE);
d.draw_text("Hello, raylib-rs!", 20, 20, 20, Color::BLACK);
}
}
Walking through the code:
raylib::init()returns aRaylibBuilder. Chain.size()and.title()to configure the window before it opens..build()opens the window and returns a pair:RaylibHandle(your main API handle) andRaylibThread(a token proving you are on the main thread).rl.begin_drawing(&thread)opens a drawing frame and returns aRaylibDrawHandle. All draw calls live inside this block; the frame is committed whendis dropped at the end of the loop body.Color::WHITEandColor::BLACKare constants inraylib::prelude. No import gymnastics needed.
What’s next
- Install guides — pick your platform: Windows, macOS, Linux, or Web/Wasm.
- Core Concepts — the Handle/Thread ownership model, RAII resources, and how strings work.
- Modules — dive into Window and drawing, Input, Shapes, and beyond.
Install on Windows
raylib-rs builds on Windows using the MSVC toolchain (Visual Studio Build Tools). The MSVC linker and Windows SDK are required; the MinGW/GNU toolchain is not supported.
Prerequisites
-
Rust 1.85+ — install via rustup. When rustup asks which toolchain to install, choose
stable-x86_64-pc-windows-msvc(the default on Windows). -
Visual Studio Build Tools 2019 or newer — download the Build Tools for Visual Studio installer. During setup, select the “Desktop development with C++” workload. This provides
cl.exe, the Windows SDK headers, and the linker. -
CMake 3.15+ — download from cmake.org and ensure
cmakeis on yourPATH. CMake is required by theraylib-sysbuild script to compile the vendored raylib C source.
Install steps
# 1. Install rustup (if not already installed)
# Download from https://rustup.rs/ and run the installer.
# 2. Confirm the MSVC toolchain is active
rustup show
# 3. Create a new project
cargo new my-raylib-game
cd my-raylib-game
# 4. Add raylib as a dependency
cargo add raylib
# 5. Build
cargo build
If the build succeeds you will see raylib compile and link. Replace src/main.rs with the hello-window snippet from the Quickstart and run with cargo run.
Windowing and graphics back end
raylib-rs uses GLFW + OpenGL 3.3 by default on Windows. The wayland and drm features are Linux-only and have no effect here.
Troubleshooting
-
“cannot find libclang” or bindgen errors — bindgen requires a working LLVM/clang installation on some paths. Install the LLVM toolchain for Windows and set
LIBCLANG_PATHto the LLVMbindirectory. See the bindgen requirements docs and the project backlog issue #254 for known issues. -
“link.exe not found” or linker errors — confirm that Visual Studio Build Tools are installed with the “Desktop development with C++” workload. Run
rustup default stable-x86_64-pc-windows-msvcto ensure you are on the MSVC toolchain. -
CMake not found — ensure
cmake --versionworks in a new terminal. Add CMake’sbindirectory to yourPATHor reinstall CMake with the “Add CMake to the system PATH” option checked.
Install on macOS
raylib-rs builds on macOS with the default Apple clang toolchain. Both Apple Silicon (M1/M2/M3) and Intel Macs are supported.
Prerequisites
-
Rust 1.85+ — install via rustup.
-
Xcode Command Line Tools — provides
clang,ar, and the macOS SDK headers. Install once with:xcode-select --installIf you already have a full Xcode install, the command line tools are bundled.
-
CMake 3.15+ — the easiest way is via Homebrew:
brew install cmakeAlternatively, download the macOS DMG from cmake.org and add the
bindirectory to yourPATH.
Install steps
# 1. Create a new project
cargo new my-raylib-game
cd my-raylib-game
# 2. Add raylib as a dependency
cargo add raylib
# 3. Build
cargo build
Replace src/main.rs with the hello-window snippet from the Quickstart and run with cargo run.
Apple Silicon vs Intel
Both architectures use the same build path. On Apple Silicon, macOS routes OpenGL calls through Apple’s Metal compatibility layer — performance is excellent for most use cases. The wayland and drm features are Linux-only and have no effect on macOS.
Troubleshooting
-
Framework linker errors (
ld: framework not found OpenGLor similar) — your Xcode Command Line Tools may be out of date. Runxcode-select --installto reinstall, or update Xcode from the App Store, then re-runsudo xcode-select -s /Applications/Xcode.app/Contents/Developer. -
xcrun: error: SDK not found— ensure the macOS SDK is present. Runxcrun --show-sdk-pathto check. If it errors, reinstall the Command Line Tools. -
Slow first build — the
raylib-sysbuild script compiles the vendored raylib C source with CMake. Subsequent builds use the incremental cache and are much faster.
Install on Linux
raylib-rs builds on Linux using the system GCC or Clang toolchain. You need the X11 and/or Wayland development headers, CMake, and the OpenGL headers. The exact package names differ by distribution.
Prerequisites
-
Rust 1.85+ — install via rustup.
-
CMake 3.15+ and build essentials — see the distribution-specific instructions below.
-
Native graphics headers — X11 + OpenGL are required for the default build. Wayland headers are optional (only needed for the
waylandfeature).
Ubuntu / Debian
sudo apt-get update
sudo apt-get install --no-install-recommends -y \
cmake \
libasound2-dev \
libudev-dev \
libx11-dev \
libxrandr-dev \
libxinerama-dev \
libxcursor-dev \
libxi-dev \
libgl1-mesa-dev
This is the exact package list used by the CI check.yml, test.yml, and book.yml workflows.
Fedora / RHEL / AlmaLinux
sudo dnf install -y \
cmake \
gcc-c++ \
alsa-lib-devel \
systemd-devel \
libX11-devel \
libXrandr-devel \
libXinerama-devel \
libXcursor-devel \
libXi-devel \
mesa-libGL-devel
Arch Linux
sudo pacman -S --needed \
cmake \
base-devel \
alsa-lib \
systemd-libs \
libx11 \
libxrandr \
libxinerama \
libxcursor \
libxi \
mesa
Build and run
cargo new my-raylib-game
cd my-raylib-game
cargo add raylib
cargo build
Replace src/main.rs with the hello-window snippet from the Quickstart and run with cargo run.
Wayland and DRM
- Wayland — opt in with
--features wayland. Additionally requires:- Ubuntu/Debian:
libwayland-dev libxkbcommon-dev - Fedora:
wayland-devel libxkbcommon-devel - Arch:
wayland libxkbcommon
- Ubuntu/Debian:
- DRM / tty rendering — opt in with
--features drm,opengl_es_20. Renders directly to the framebuffer without a display server; useful for embedded/kiosk targets.
Troubleshooting
-
cannot find libclang— installclangandlibclang-dev(Ubuntu:sudo apt-get install clang libclang-dev; Fedora:sudo dnf install clang). Set theLIBCLANG_PATHenvironment variable if bindgen still cannot find it. -
cmake: command not found— install cmake from your package manager (see above). -
Wayland: display server not detected — if you are running under X11 and get Wayland compositor errors, ensure you are either using the
waylandfeature intentionally or have omitted it (X11 is the default).
Install for the Web
raylib-rs targets wasm32-unknown-emscripten, which compiles your game to WebAssembly and runs it in a browser via OpenGL ES 2 (WebGL). This path is build-verified continuously in the web.yml CI workflow.
Prerequisites
-
Rust 1.85+ — install via rustup.
-
The wasm32-unknown-emscripten target:
rustup target add wasm32-unknown-emscripten -
emsdk 3.1.64+ — the Emscripten SDK provides
emcc, the compiler that cross-compiles C to WebAssembly. Follow the official emsdk install instructions:git clone https://github.com/emscripten-core/emsdk.git cd emsdk ./emsdk install 3.1.64 ./emsdk activate 3.1.64 source ./emsdk_env.sh # add emcc to PATH for this shell session
Required environment variable
The raylib-sys build script requires EMCC_CFLAGS to be set when targeting wasm32-unknown-emscripten. Set it before building:
export EMCC_CFLAGS="-O3 -sUSE_GLFW=3 -sASSERTIONS=1 -sWASM=1 -sASYNCIFY -sGL_ENABLE_GET_PROC_ADDRESS=1"
This exact value is used by the CI web.yml workflow.
Build
cargo build -p raylib --target wasm32-unknown-emscripten
This compiles the raylib crate (and the vendored raylib C source) for the WebAssembly target.
Running in a browser
A cargo build produces a .wasm binary and a .js glue file, but running in a browser also requires an HTML shell that loads them. raylib provides an HTML shell template. The WS9 showcase site (the final workstream of the 6.0 upgrade) will provide working web examples and a reference HTML shell.
Known limitation
The software_renderer feature (PLATFORM=Memory / rlsw) is not currently supported on wasm32-unknown-emscripten. The software renderer is a headless desktop-testing path; WebAssembly uses WebGL (GLES2), not the CPU-only rlsw backend. This is a tracked-deferred item from WS6b; see docs/superpowers/notes/ws6b-complete.md item 5.
Known-unsupported / future work
The following targets are tracked in the project backlog but are not currently supported. Builds for these targets may fail or produce incorrect output.
-
aarch64 Linux — the build is currently broken on 64-bit ARM Linux. Tracked in issue #227.
-
Cross-compile Linux → Windows (debug mode) — cross-compilation from Linux to Windows targets fails in debug mode. Tracked in issue #181.
-
NixOS X11 — building under NixOS with the X11 back end requires extra pkgs configuration not yet covered by the official build instructions. Tracked in PR #289 and issue #288. This target is part of the WS6 platform matrix.
-
software_rendereronwasm32-unknown-emscripten— the CPU-only headless renderer (PLATFORM=Memory/ rlsw) is incompatible with the WebAssembly target. Tracked as a deferred item indocs/superpowers/notes/ws6b-complete.md(item 5).
These are tracked in the project backlog; community PRs welcome.
Handle and thread
RaylibHandle is the entry point for almost every raylib operation. Together with RaylibThread, it
is returned by raylib::init().build(). The two-value return enforces two invariants at the type
level: there can only be one active window at a time (single-init), and drawing can only happen on
the thread that owns the handle.
RaylibHandle owns the window and the OpenGL context. RaylibThread is a zero-sized marker token
that proves you are on the main thread. You pass &thread into begin_drawing and other
thread-sensitive calls; the borrow checker ensures they are never called from a background thread.
API surface
RaylibHandle— the main API handle; returned alongsideRaylibThreadfrom.build().RaylibThread— main-thread proof token;!Send.RaylibBuilder— builder for the window; obtained fromraylib::init().raylib::init()— the only entry point; returns aRaylibBuilder.RaylibHandle::window_should_close— returnstruewhen the user requests quit (window X,Escape, etc.).RaylibHandle::begin_drawing— opens a drawing frame; returns aRaylibDrawHandleguard that commits the frame on drop.
Example
The following shows the canonical init-loop-draw pattern, highlighting where the handle and thread token appear:
extern crate raylib;
use raylib::prelude::*;
fn main() {
// init() returns a RaylibBuilder; .build() opens the window.
// The two return values are the handle (rl) and the thread token (thread).
let (mut rl, thread) = raylib::init()
.size(640, 480)
.title("Handle and thread demo")
.build();
while !rl.window_should_close() {
// begin_drawing takes &thread — the borrow checker ensures this is
// the main thread. The returned guard commits the frame on drop.
let mut d = rl.begin_drawing(&thread);
d.clear_background(Color::RAYWHITE);
d.draw_text("RaylibHandle in action", 20, 20, 20, Color::DARKGRAY);
}
// rl is dropped here, tearing down the window and GL context.
}
Gotchas
RaylibThreadis!Send— never put it in aMutex,Arc, orthread::spawnpayload. The compiler will reject the attempt; this is intentional.- Single-init — calling
raylib::init().build()while an existingRaylibHandleis live will panic. raylib’s C layer maintains global window state; there is no way to have two windows in one process. - Teardown order — window teardown happens when
RaylibHandleis dropped. If youmem::forgetthe handle, raylib’s GL context leaks. Let Rust’s normal drop order handle cleanup; do not try to tear down the window manually.
See also
core-concepts/raii-and-resources.md— how all raylib resources follow RAII semantics.modules/window-and-drawing.md— the full window configuration and drawing API.- docs.rs
RaylibHandle
RAII and resources
Every raylib resource in raylib-rs is a Rust struct that cleans up on drop. The Unload* family of
C functions is never exposed in the public API — Drop implementations call them automatically
when the resource leaves scope. This eliminates the class of bug where a resource is forgotten or
freed twice.
The pattern applies uniformly: Image, Texture2D, RenderTexture2D, Font, Mesh, Shader,
Material, and Model all implement Drop. You allocate with load_* or gen_image_*; you free
by letting the value go out of scope (or by calling drop() explicitly when order matters).
API surface
Image— CPU-side pixel buffer; dropped by callingUnloadImage.Texture2D— GPU texture handle; dropped by callingUnloadTexture.RenderTexture2D— off-screen render target; dropped byUnloadRenderTexture.Font— loaded font; dropped byUnloadFont.Mesh— vertex data; owned byModel(do not drop separately).Shader— GPU shader program; dropped byUnloadShader.Material— material parameters; owned byModel.Model— 3D model including meshes and materials; dropped byUnloadModel.ModelAnimations— new in 6.0, RAII wrapper for the skeletal-animation animation set.
Example
The example below shows resource loading and explicit teardown order. image is dropped first;
font is kept alive until the end of the outer scope. Because Drop is deterministic in Rust, the
sequence is predictable and correct.
extern crate raylib;
use raylib::prelude::*;
fn main() {
let (mut rl, thread) = raylib::init()
.size(640, 480)
.title("RAII demo")
.build();
// gen_image_color returns an Image that owns its pixel buffer.
let image = Image::gen_image_color(64, 64, Color::RED);
// load_texture_from_image uploads to the GPU.
let texture = rl.load_texture_from_image(&thread, &image).unwrap();
// Explicit drop: image is freed here (UnloadImage called).
// texture remains alive.
drop(image);
// texture is freed here when it leaves scope (UnloadTexture called).
drop(texture);
// RaylibHandle (rl) is freed here, tearing down the window.
}
Gotchas
- Lifetime-bound audio resources —
Wave,Sound,Music, andAudioStreamare bound by lifetime to aRaylibAudio. They cannot outlive the audio device. See the audio chapter. - raylib-allocated buffers — functions that return raylib-managed byte slices (e.g., exported
image data) wrap them as
ManuallyDrop<Box<[T]>>and free via the matchingUnload*orMemFreecall — neverlibc::free. This stays correct under custom raylib allocators. SeeDECISIONS.mdfor the rationale. Modelowns itsMeshandMaterialarrays — do not calldrop()on aMeshorMaterialthat came from aModel. Dropping theModelhandles everything.
See also
core-concepts/handle-and-thread.md— why the handle matters for resource loading.modules/textures-and-images.md—Imagevs.Texture2Din depth.modules/audio.md— lifetime-bound audio resources.
Strings and allocations
raylib is a C API, so strings cross an FFI boundary on almost every call. raylib-rs handles the two different use cases separately:
- Most API functions accept
&strand return ownedString. The binding converts to a null-terminatedCStringinternally. This is ergonomic and safe; the small allocation is acceptable for infrequent calls like loading a file or setting a window title. - Per-frame GUI draw functions take
&CStrto avoid allocations inside the render loop. Therstr!macro constructs a&CStrat compile time from a string literal — zero allocation, zero runtime cost.
The WS5 raygui binding goes further: raygui’s string parameters use impl AsRef<str> signatures
backed by a thread-local scratch buffer. This means you can pass &str or String directly to
GUI calls without wrapping them in rstr!.
API surface
rstr!— constructs a&'static CStrfrom a string literal at compile time, or aCStringfrom a format string.&CStrparameters — the low-level string type used by per-frame GUI draw functions.&strparameters — the ergonomic string type used by most load/config functions.impl AsRef<str>— the raygui parameter convention (WS5); accepts&str,String, and anything that derefs tostr.
Example
rstr! is the idiomatic way to create a &CStr literal without a heap allocation:
#![allow(unused)]
fn main() {
extern crate raylib;
use std::ffi::CStr;
// rstr! is a macro exported from the raylib crate root.
let label: &CStr = raylib::rstr!("hello");
assert_eq!(label.to_bytes(), b"hello");
}
The compile-time form (rstr!("literal")) embeds a null byte and wraps the bytes as &CStr
without any runtime allocation. The runtime form (rstr!("{} items", count)) allocates a
CString and is suitable for dynamic strings outside the hot path.
Gotchas
- Per-frame GUI calls use
&CStr— userstr!for compile-time labels. For dynamic strings inside a frame, prefer theimpl AsRef<str>raygui API (WS5) which uses a thread-local buffer and avoids a heap allocation on every call. - Embedded nul bytes — backlog #220 notes that
OsStr::to_string_lossydoes not replace embedded nul bytes. raylib-rs validates its string inputs, but be aware that C raylib will truncate at the first nul if a nul somehow reaches the C side. - raylib-allocated buffers — some functions return raylib-managed byte slices wrapped as
ManuallyDrop<Box<[T]>>. These must be freed via the matchingUnload*orMemFree, notlibc::free. SeeDECISIONS.md.
See also
core-concepts/safety.md— why FFI calls require careful handling.modules/raygui.md— theimpl AsRef<str>raygui convention.modules/text-and-fonts.md— text rendering and font loading.
Safety
raylib-rs is a safe Rust wrapper over a C library. The safe API surface — everything in
raylib::prelude — never requires unsafe from the caller. Internally, unsafe is used sparingly
and always justified.
The project follows two conventions enforced by code review:
- Every
unsafe fnhas a/// # Safetydoc that states the preconditions the caller must uphold. - Every
unsafe { ... }block has a// SAFETY:comment explaining why the specific call is sound at that site.
These conventions make it easy to audit the unsafety budget during reviews and when upgrading raylib’s C version.
API surface
Conventions, not API items — this chapter describes the disciplines baked into the codebase. Key structural choices that influence safety:
- RAII wrappers (
Image,Texture2D,Font, …) ensure resources are freed exactly once. No raw pointers are exposed to callers. RaylibThread: !Send + !Sync— the main-thread token is not sendable, so thread-sensitive raylib calls can only be made from the thread that calledinit().- No
Send/Syncfor pointer-owning wrappers — types that own opaque C pointers do not implementAsRef/AsMutin ways that could allow aliased mutable access (partial WS6 fix for #277). - Audio lifetimes —
Sound,Music, andAudioStreamcarry lifetime parameters tying them to theRaylibAudiothat created them; the borrow checker prevents use-after-free.
Example
The excerpt below is taken from raylib/src/rlgl/mod.rs and shows both conventions in action:
/// Push the current matrix onto the stack; the returned guard pops it on drop.
/// Apply transforms (rl_translatef/rl_rotatef/rl_scalef) through the guard.
#[inline]
fn rl_push_matrix(&mut self) -> RlMatrix<'_, Self> {
// SAFETY: the returned RlMatrix's Drop calls the matching rlPopMatrix.
unsafe { ffi::rlPushMatrix() };
RlMatrix::new(self)
}
/// Reset the current matrix to identity.
#[inline]
fn rl_load_identity(&mut self) {
// SAFETY: rlLoadIdentity is an unconditional state mutation; no preconditions.
unsafe { ffi::rlLoadIdentity() }
}
rl_push_matrix is itself a safe function — callers need no unsafe. The // SAFETY: comment
explains the invariant: the RlMatrix guard’s Drop impl guarantees the matching rlPopMatrix
call, so the push/pop pair is always balanced.
A full unsafe fn with a # Safety doc follows the same pattern — see
raylib/src/core/databuf.rs for the RlManaged type, where each constructor documents its
preconditions on pointer provenance and allocator compatibility.
Gotchas
- Don’t assume FFI calls are safe because the C looks innocuous. raylib’s C functions carry
implicit preconditions (e.g., window must be initialised, texture must be uploaded, pointer
must not be null). The binding encodes these into the Rust type system where possible; where it
cannot (raw FFI escape hatches),
# Safetydocs spell them out. AsRef/AsMutunsoundness — pointer-owning wrappers do not implementAsRef/AsMutin ways that could permit aliased mutation of the same C object from multiple Rust references. The WS6-prep partial fix (#277) removed the most dangerous impls; a full refactor is tracked-deferred.- Skeletal-animation Drop — the pre-6.0 code called
UnloadModelAnimationsvia a raw*mutpointer in a way that could double-free. The WS3 redesign introducedModelAnimationsas a proper RAII struct with a safeDropimpl. If you are upgrading from 5.x, replace any manual animation teardown with the new type.
See also
CONTRIBUTE.md— the full coding conventions including theunsafediscipline.core-concepts/raii-and-resources.md— how RAII enforces resource safety.- docs.rs unsafe functions — every
unsafe fnin the public API lists its# Safetyrequirements.
Features and platforms
raylib-rs uses Cargo features to gate platform back-ends, optional math integrations, and the
software renderer. The default feature set mirrors raylib 6.0’s config.h SUPPORT_* configuration:
everything a desktop OpenGL 3.3 application needs, and nothing it doesn’t.
Optional integrations — glam, mint, serde — are strictly opt-in. A default build carries zero
math-crate dependencies. The full feature alias is the canonical “everything except mutually-
exclusive back-ends” target used for CI linting and testing.
Important:
--all-featuresis invalid forraylib-sys. Always use--features fullwhen you want maximum capability in a single build invocation.
API surface
Feature flags (headline set):
opengl_33— OpenGL 3.3 core; this is the default back-end on desktop.opengl_21— OpenGL 2.1 compatibility; older hardware and some embedded targets.opengl_es_20— OpenGL ES 2.0; mobile / DRM back-end. Use withdrm.drm— DRM/KMS tty rendering; pair withopengl_es_20.wayland— Wayland display server support (Linux).software_renderer— CPU software renderer (PLATFORM=Memory, rlsw). Mutually exclusive withopengl_*. Enables theraylib::test_harnessheadless render-test module.glam—From/Intoconversions between raylib’sVector*/Matrix/Quaternionandglam::Vec*/glam::Mat4/glam::Quat.mint—From/Intoconversions withmint::Vector*/mint::ColumnMatrix*.serde—Serialize/Deserializederives on math and color types.nobuild— skip building the vendored raylib C source (you supply the link target).nobindgen— skip re-generatingraylib-sysbindings (read fromRAYLIB_BINDGEN_LOCATION).full— curated maximum-capability alias (excludes mutually-exclusive back-ends and escape hatches); use this forcargo test --features fulland CI.
Example
Three common Cargo.toml stanzas:
# 1. Default desktop build (OpenGL 3.3, no optional adapters)
[dependencies]
raylib = "6.0.0-rc.2"
# 2. Headless / CI build using the software renderer (no GPU required)
[dependencies]
raylib = { version = "6.0.0-rc.2", features = ["software_renderer"] }
# 3. Game with glam math and serde serialization
[dependencies]
raylib = { version = "6.0.0-rc.2", features = ["glam", "serde"] }
Gotchas
opengl_*features are mutually exclusive — by convention, select only one. The build script does not currently diagnose multiple selections (the last#[cfg]wins), so enabling more than one is a footgun.software_rendereris mutually exclusive withopengl_*. The two back-ends cannot be compiled into the same binary. It is also currently incompatible withwasm32-unknown-emscripten— tracked-deferred; seenotes/ws6b-complete.md.--all-featuresis invalid forraylib-sysbecause it would try to enable multiple mutually-exclusive back-ends simultaneously. Use--features fullinstead.SUPPORT_*flags — individual raylib capability flags (e.g.,SUPPORT_IMAGE_GENERATION,SUPPORT_FILEFORMAT_PNG) map directly to Cargo features of the same name. They are included infulland usually do not need to be set explicitly.
See also
- Install on Windows, macOS, Linux, Web — platform-specific dependency setup.
modules/software-renderer.md— the headless render-test harness.ecosystem/glam-mint-serde.md— the math-ecosystem integration features.
Window and drawing
The window is opened by calling raylib::init().build(). The builder returns a
(RaylibHandle, RaylibThread) pair that controls the entire lifecycle: the handle
exposes every per-frame operation, and the thread token is the proof that you are on
the correct OS thread. Drawing happens between begin_drawing and the end of the
returned guard’s lifetime — all draw primitives live on the RaylibDraw trait
implemented by RaylibDrawHandle.
API surface
RaylibBuilder— fluent builder returned byraylib::init(); call.build()to open the window. Key builder methods:.size(w, h),.title(s),.vsync(),.msaa_4x(),.undecorated().RaylibHandle— the main handle; owns the window and is the receiver for most API calls.RaylibHandle::set_target_fps— cap the frame rate.set_target_fps(0)disables the limiter (busy-spin).RaylibHandle::begin_drawing— returns a scopedRaylibDrawHandle;EndDrawingfires automatically on drop.RaylibDraw— trait with all 2D drawing primitives:clear_background,draw_text,draw_rectangle,draw_line,draw_circle,draw_texture, and more.RaylibHandle::window_should_close— returnstruewhen the OS close event has been sent.
Example
extern crate raylib;
use raylib::prelude::*;
fn main() {
let (mut rl, thread) = raylib::init()
.size(640, 480)
.title("Hello, raylib-rs!")
.vsync()
.build();
rl.set_target_fps(60);
while !rl.window_should_close() {
let mut d = rl.begin_drawing(&thread);
d.clear_background(Color::RAYWHITE);
d.draw_text("Hello, world!", 12, 12, 20, Color::DARKGRAY);
}
// RaylibHandle is dropped here; CloseWindow() is called automatically.
}
Gotchas
- Draw only inside the guard. All methods on
RaylibDraware only accessible on theRaylibDrawHandlescope. You cannot calldraw_textdirectly onRaylibHandle— you must enterbegin_drawingfirst. - FPS pacing.
set_target_fps(0)removes the frame limiter entirely; raylib will busy-spin. Omitting the call defaults to the raylib default (60 on most platforms). - Window state queries.
is_window_minimized,is_window_resized, and related helpers live on theRaylibWindowtrait — check the trait’s documentation for the current set of methods. RaylibThreadis!Send. Never put it in aMutex,Arc, or hand it to another thread. See the Handle and thread chapter for why.
See also
- Handle and thread — lifecycle details and
the
!Sendinvariant. - Input — reading keyboard, mouse, and gamepad inside the loop.
- Shapes — the full 2D primitive catalogue.
Showcase examples
Showcase examples that exercise this module:
- core_basic_window
- core_window_flags
- core_window_letterbox
- core_window_should_close
- core_2d_camera
- core_basic_screen_manager
Input
Input in raylib-rs is queried per-frame off RaylibHandle — there is no callback or
event queue. Every call snapshots the state that raylib collected during the previous
EndDrawing call. The API covers keyboard, mouse, gamepad, and touch in a uniform
style: is_*_down for level queries and is_*_pressed/is_*_released for
edge-triggered queries.
API surface
RaylibHandle::is_key_down—truewhile a key is held.RaylibHandle::is_key_pressed—trueon the first frame a key transitions to down (edge-triggered).RaylibHandle::get_mouse_position— returns aVector2in window pixels, Y-down.RaylibHandle::get_mouse_wheel_move— returns scroll delta for the current frame.RaylibHandle::is_gamepad_available— checks whether a gamepad index is connected.RaylibHandle::get_gamepad_button_pressed— returnsOption<GamepadButton>for the most recently pressed button.KeyboardKey— enum of all keyboard scancodes (e.g.,KeyboardKey::KEY_SPACE).MouseButton— left / right / middle and extended buttons.GamepadButton— cross-platform gamepad button enum.
Example
extern crate raylib;
use raylib::prelude::*;
fn main() {
let (mut rl, thread) = raylib::init()
.size(640, 480)
.title("Input demo")
.build();
while !rl.window_should_close() {
// Level query — true every frame the key is held.
if rl.is_key_down(KeyboardKey::KEY_SPACE) {
println!("SPACE held");
}
// Edge query — true only on the first frame of a press.
if rl.is_key_pressed(KeyboardKey::KEY_ENTER) {
println!("ENTER pressed");
}
let mouse = rl.get_mouse_position();
let scroll = rl.get_mouse_wheel_move();
let mut d = rl.begin_drawing(&thread);
d.clear_background(Color::RAYWHITE);
d.draw_text(
&format!("mouse: ({:.0}, {:.0}) scroll: {:.1}", mouse.x, mouse.y, scroll),
10, 10, 20, Color::DARKGRAY,
);
}
}
Gotchas
is_key_pressedis per-frame edge-triggered. It returnstrueonly on the single frame where the key transitions from up to down. If you poll it every frame and hold the key, you will see exactly onetrueunless you also checkis_key_pressed_repeatfor held-down auto-repeat.- Mouse coordinates are Y-down.
get_mouse_position().yincreases downward, which matches window pixel conventions but is the opposite of mathematical Y-up. get_gamepad_button_pressedtransmute UB (tracked-deferred). The current implementation performs atransmute::<u32, GamepadButton>on the raw integer returned by raylib. If raylib returns a value outside the knownGamepadButtonvariants, this is undefined behaviour. A soundness fix is tracked for a future PR. For now, rely only onis_gamepad_button_pressed/is_gamepad_button_downwith explicitGamepadButtonvariants.
See also
- Window and drawing — the frame loop that input is polled inside.
KeyboardKeydocs.rs — full list of key constants.
Showcase examples
Showcase examples that exercise this module:
- core_input_keys
- core_input_mouse
- core_input_mouse_wheel
- core_input_gamepad
- core_input_gestures
- core_keyboard_testbed
Shapes
2D drawing primitives — rectangles, lines, circles, triangles, polygons, and
individual pixels — are all methods on the
RaylibDraw
trait. The trait is implemented by RaylibDrawHandle, so all calls live inside the
begin_drawing scope. No extra state or resource loading is required; shapes are
drawn immediately with a color and optional parameters for thickness, rounding, or
gradients.
API surface
draw_rectangle— filled rectangle by position + size.draw_rectangle_v— filled rectangle byVector2position + size.draw_rectangle_rec— filled rectangle from aRectanglevalue.draw_circle— filled circle by center + radius.draw_circle_v— filled circle byVector2center + radius.draw_line— 1-pixel line between two integer points.draw_line_ex— thick line between twoVector2points with athicknessparameter.draw_triangle— filled triangle from threeVector2vertices (counter-clockwise winding).draw_poly— filled regular polygon (n sides).draw_pixel— single pixel by integer coordinates.
Example
The example opens a window and draws a rectangle and a circle every frame. A software-renderer version of this is available as part of the WS9 showcase; the live example here requires a display.
extern crate raylib;
use raylib::prelude::*;
fn main() {
let (mut rl, thread) = raylib::init()
.size(640, 480)
.title("Shapes demo")
.vsync()
.build();
while !rl.window_should_close() {
let mut d = rl.begin_drawing(&thread);
d.clear_background(Color::RAYWHITE);
// Filled rectangle at (50, 50), 200×100, blue.
d.draw_rectangle(50, 50, 200, 100, Color::BLUE);
// Filled circle at (400, 200), radius 80, red.
d.draw_circle(400, 200, 80.0, Color::RED);
// 1-pixel line from (0, 0) to (640, 480), dark gray.
d.draw_line(0, 0, 640, 480, Color::DARKGRAY);
}
}
Gotchas
Shapes are stable across raylib 5.x and 6.0; there are no breaking changes to the 2D primitive set. The only points worth noting:
- Counter-clockwise winding for
draw_triangle. raylib expects vertices in counter-clockwise order; clockwise-wound triangles are culled. - Integer vs. vector variants. Most primitives have both an integer overload
(
draw_circle(cx: i32, cy: i32, …)) and a_voverload (draw_circle_v(center: Vector2, …)). Prefer_vwhen your coordinates are alreadyVector2.
See also
- Window and drawing — the
begin_drawingscope that all shape calls live inside. - Textures and images — blitting pixel buffers and textures in the same draw scope.
- Software renderer — running headless shape tests.
Showcase examples
Showcase examples that exercise this module:
- shapes_basic_shapes
- shapes_lines_bezier
- shapes_rectangle_advanced
- shapes_splines_drawing
- shapes_colors_palette
- shapes_logo_raylib
Textures and images
raylib distinguishes two related types: Image is CPU-side pixel data (a
heap-allocated buffer you can read and modify without a GPU or a window), and
Texture2D is a GPU-side handle created by uploading an Image. The typical
flow is: generate or load an Image, manipulate it, then call
load_texture_from_image on RaylibHandle to push it to the GPU.
Both types are RAII resources — they clean up automatically on drop (see RAII and resources).
API surface
Image::load_image— load an image file from disk; returnsResult<Image, …>.Image::gen_image_color— generate a solid-color image in memory; no window needed.Image::gen_image_perlin_noise— procedural Perlin-noise image.Image::draw— CPU-side blit of one image onto another.Image::get_color— read a single pixel’sColorby(x, y).RaylibHandle::load_texture_from_image— upload anImageto the GPU as aTexture2D; requires a window + thread token.RenderTexture2D— off-screen render target; used for render-to-texture workflows.RaylibDraw::draw_texture— draw aTexture2Dat a position with a tint color.
Example
CPU-side image operations (generation and pixel read-back) work without a window or GPU. The example below generates a red image and asserts a pixel value — no display required.
extern crate raylib;
use raylib::prelude::*;
fn main() {
// Generate a 64×64 solid-red Image entirely in CPU memory.
let img = Image::gen_image_color(64, 64, Color::RED);
// Read back a pixel and verify its color.
let pixel = img.get_color(32, 32);
assert_eq!(pixel.r, 255);
assert_eq!(pixel.g, 0);
assert_eq!(pixel.b, 0);
// `img` is dropped here; UnloadImage is called automatically.
}
Uploading to the GPU requires a window:
extern crate raylib;
use raylib::prelude::*;
fn main() {
let (mut rl, thread) = raylib::init()
.size(640, 480)
.title("Texture demo")
.build();
let img = Image::gen_image_color(64, 64, Color::BLUE);
// load_texture_from_image uploads to GPU; img can be dropped afterwards.
let tex = rl.load_texture_from_image(&thread, &img).unwrap();
drop(img); // CPU buffer freed; GPU copy lives on in `tex`.
while !rl.window_should_close() {
let mut d = rl.begin_drawing(&thread);
d.clear_background(Color::RAYWHITE);
d.draw_texture(&tex, 100, 100, Color::WHITE);
}
}
Gotchas
Imageis CPU;Texture2Dis GPU. You cannot modify aTexture2Ddirectly. If you need per-frame CPU edits, keep theImage, modify it, and re-upload withupdate_texture.RenderTexture2DY-flip on readback. The WS4 headless harness reads the software-renderer framebuffer as BGRA with Y-inverted rows and then normalises it for you. If you useload_image_from_screendirectly, be aware of the GL-style bottom-left origin. Seenotes/ws4b-complete.md.- Memory-leak fix in 6.0.
export_image_to_memoryhad a leak in 5.x (backlog #247). It was fixed by PR #250 in WS3 — the buffer is now wrapped asManuallyDrop<Box<[u8]>>and freed correctly.
See also
- Software renderer — headless pixel-probe testing.
- 3D models — textures applied to models.
Imagedocs.rs /Texture2Ddocs.rs
Showcase examples
Showcase examples that exercise this module:
- textures_image_loading
- textures_image_drawing
- textures_image_generation
- textures_sprite_animation
- textures_npatch_drawing
- textures_bunnymark
Text and fonts
raylib bundles a default bitmap font that is available as soon as the window is open.
Custom fonts are loaded via RaylibHandle::load_font (from a file) or
RaylibHandle::load_font_ex (with an explicit codepoint set for preloaded glyph
atlases). Both return a RAII Font that is automatically unloaded on drop.
Text drawing — including measuring — hangs off RaylibHandle for the default font
and off the Font struct or RaylibDraw trait for custom fonts.
API surface
RaylibHandle::load_font— load a font from a file; returnsResult<Font, …>.RaylibHandle::load_font_ex— load a font with explicit font size and a codepoint array; useful for preloading a specific non-ASCII character set.RaylibHandle::measure_text— measure the pixel width of a string at a given size using the default font.Font::measure_text— measure a string with a custom font, returning aVector2(width × height).RaylibDraw::draw_text— draw a&strusing the default font.RaylibDraw::draw_text_ex— draw a&strusing a customFont, with position, size, spacing, and tint.
Example
Drawing text requires an open window. The example below opens a 640×480 window, draws a greeting with the default font each frame, and measures its width for centering.
extern crate raylib;
use raylib::prelude::*;
fn main() {
let (mut rl, thread) = raylib::init()
.size(640, 480)
.title("Text demo")
.vsync()
.build();
let message = "Hello, raylib-rs!";
let font_size = 24;
while !rl.window_should_close() {
// Measure text width for the default font.
let width = rl.measure_text(message, font_size);
let mut d = rl.begin_drawing(&thread);
d.clear_background(Color::RAYWHITE);
// Centered draw using the default font.
d.draw_text(
message,
(640 - width) / 2,
(480 - font_size) / 2,
font_size,
Color::DARKGRAY,
);
}
}
Gotchas
- Custom fonts need a window.
load_fontandload_font_excall into raylib’s GPU atlas-building path, so they must be called afterraylib::init().build(). Font data files (.ttf,.otf,.fnt,.bdf) must be on disk at the given path. - Codepoint preloading.
load_font_extakes an optional&[i32]codepoint list. IfNone, raylib loads the default Latin set (32–127). Pass an explicit list if your application draws CJK, emoji, or other extended Unicode ranges. - raylib 6.0 new text symbols. The 6.0 release added more than 30 new text utility
functions in the C library. The Rust parity checklist (
docs/superpowers/plans/) tracks which are exposed; check thetextmodule rustdoc for the current set.
See also
- Window and drawing — the draw handle that text methods live on.
Fontdocs.rs — full rustdoc for custom font operations.
Showcase examples
Showcase examples that exercise this module:
- text_font_loading
- text_font_sdf
- text_format_text
- text_input_box
- text_rectangle_bounds
- text_unicode_emojis
3D models
3D rendering in raylib-rs uses Camera3D for view/projection and a
RaylibMode3D scope (entered via begin_mode3D) that activates 3D draw calls.
A Model bundles one or more Mesh arrays and Material arrays into a single
RAII resource. Skeletal animation is exposed through the new 6.0 ModelAnimations
wrapper.
API surface
RaylibHandle::load_model— load a model from a file (.obj,.glb,.gltf,.iqm, …); returnsResult<Model, …>.Mesh— vertex data; owned byModel— do not drop it separately.Material— material parameters (textures, colours, shader); owned byModel.ModelAnimations— RAII wrapper (new in 6.0) for the skeletal-animation set returned byload_model_animations.Camera3D— position, target, up-vector, FOV, and projection mode.RaylibMode3DExt::begin_mode3D— enters 3D mode; returns aRaylibMode3Dguard that callsEndMode3Don drop.RaylibDraw3D::draw_model— draw a model at a position with a scale and tint.
Example
extern crate raylib;
use raylib::prelude::*;
fn main() {
let (mut rl, thread) = raylib::init()
.size(800, 600)
.title("3D model demo")
.vsync()
.build();
let model = rl.load_model(&thread, "assets/robot.glb").unwrap();
let camera = Camera3D {
position: Vector3::new(5.0, 5.0, 5.0),
target: Vector3::new(0.0, 0.0, 0.0),
up: Vector3::new(0.0, 1.0, 0.0),
fovy: 45.0,
projection: CameraProjection::CAMERA_PERSPECTIVE,
};
while !rl.window_should_close() {
let mut d = rl.begin_drawing(&thread);
d.clear_background(Color::RAYWHITE);
{
let mut d3 = d.begin_mode3D(camera);
d3.draw_model(&model, Vector3::zero(), 1.0, Color::WHITE);
}
}
// `model` is dropped here; UnloadModel is called automatically.
}
Gotchas
ModelAnimationsis the new RAII wrapper (6.0). Prior to 6.0, you had to callUnloadModelAnimationsmanually; the newModelAnimationsstruct handles this on drop. Load animations viaRaylibHandle::load_model_animations.Modelowns itsMeshandMaterialarrays. Do not calldrop()on aMeshorMaterialobtained from aModel— theModel’s ownDropimpl handles everything. Dropping them separately is a double-free.- Mesh accessor soundness (WS6-prep). PRs #257/#118/#256 (folded in WS6) changed
the safe accessors to return
&[T]slices whose length is guaranteed by the C struct. Out-of-bounds access is no longer possible through the safe API. - Tracked-deferred. Issue #283 (improper index calculation) and #207 (broader 3D API improvements) are deferred to future workstreams.
See also
- Textures and images — textures applied to model materials.
- raymath —
Vector3,Matrix,Quaternionused throughout the 3D API. Modeldocs.rs
Showcase examples
Showcase examples that exercise this module:
- models_loading
- models_loading_gltf
- models_mesh_generation
- models_geometric_shapes
- models_billboard_rendering
- models_box_collisions
Audio
Audio in raylib-rs sits behind a separate device token:
RaylibAudio.
Call RaylibAudio::init_audio_device() to open the audio device and get the token.
Every audio resource — Wave, Sound, Music, AudioStream — is created through
methods on RaylibAudio and carries a lifetime tied to the RaylibAudio token.
The borrow checker enforces that no audio resource outlives the device, so teardown
order is correct by construction.
Note on the plan’s wording. The plan refers to “AudioHandle” — the real type name in the codebase is
RaylibAudio(raylib::core::audio::RaylibAudio).
API surface
RaylibAudio::init_audio_device— open the audio device; returnsResult<RaylibAudio, …>. Panics if called twice.Wave— loaded wave data (raw PCM). Create viaRaylibAudio::new_wave.Sound— short audio clip ready for playback. Create viaRaylibAudio::new_sound.Music— audio stream for longer tracks. Create viaRaylibAudio::new_music.AudioStream— raw PCM stream for custom audio generation. Create viaRaylibAudio::new_audio_stream.Sound::play— play aSoundto completion (fire and forget).Music::play_stream— begin streaming aMusictrack; callupdate_music_streameach frame.AudioStream::is_processed— check whether the stream buffer needs refilling.
Example
extern crate raylib;
use raylib::prelude::*;
use raylib::core::audio::RaylibAudio;
fn main() {
let (mut rl, thread) = raylib::init()
.size(640, 480)
.title("Audio demo")
.build();
// Open the audio device.
let audio = RaylibAudio::init_audio_device()
.expect("failed to init audio device");
// Load a short sound effect; Sound is lifetime-bound to `audio`.
let sound = audio.new_sound("assets/coin.wav")
.expect("failed to load sound");
while !rl.window_should_close() {
if rl.is_key_pressed(KeyboardKey::KEY_SPACE) {
sound.play();
}
let mut d = rl.begin_drawing(&thread);
d.clear_background(Color::RAYWHITE);
d.draw_text("Press SPACE to play a sound", 10, 10, 20, Color::DARKGRAY);
}
// `sound` is dropped before `audio` — the compiler enforces this via lifetime.
// `audio` drop calls CloseAudioDevice.
}
Gotchas
RaylibAudiomust outlive every audio resource. The lifetime parameters onWave,Sound,Music, andAudioStreamare not just documentation — the compiler will reject code that dropsRaylibAudiowhile any resource is still live.- The audio callback was removed in 6.0. The old per-sample callback API is gone.
Use
AudioStreamwithis_processed/update_audio_streamfor custom PCM generation. - WS6-prep soundness fix. The unsound default
Soundimpl (backlog #277) was removed.Soundno longer implementsDefault.
See also
- RAII and resources — how lifetime-bound resources fit the broader RAII model.
RaylibAudiodocs.rs /Sounddocs.rs
Showcase examples
Showcase examples that exercise this module:
- audio_sound_loading
- audio_music_stream
- audio_raw_stream
- audio_module_playing
- audio_mixed_processor
- audio_stream_effects
raymath
raylib-rs 6.0 ships native #[repr(C)] math types directly from raylib-sys:
[Vector2], [Vector3], [Vector4], [Matrix], and [Quaternion].
Their arithmetic comes from raylib’s own raymath via a C shim
(binding/raymath_shim.c, RAYMATH_IMPLEMENTATION), wrapped as Rust
methods and operators. No third-party math crate is needed by default —
glam, mint, and serde are opt-in Cargo features.
You’ll land here when you need vector math, matrix transforms, or quaternion rotation without opening a window.
API surface
Vector2— 2D vector with.dot,.cross,.normalize,.length,.length_sqr,.rotate, and operator overloads.Vector3— 3D vector with.dot,.cross,.normalize,.length,.rotate_by_quaternion,.rotate_by_axis_angle.Vector4— 4D vector; also the FFI representation of a quaternion on the C side.Matrix— row-major 4×4 matrix (row-major in memory; see Gotchas); constructorsMatrix::identity,Matrix::translate,Matrix::rotate,Matrix::rotate_x/y/z,Matrix::rotate_xyz/zyx.Quaternion— distinct#[repr(C)]struct (C aliases it toVector4). Constructors:Quaternion::identity,Quaternion::from_axis_angle,Quaternion::normalize,Quaternion::length.Ray/BoundingBox— used by the 3D collision and raycasting helpers.lerp—f32linear interpolation convenience function.rquat— shorthand constructor forQuaternion.
Example
The math types are window-independent — this doctest runs with no GPU context.
#![allow(unused)]
fn main() {
extern crate raylib;
use raylib::math::{Vector3, Matrix};
// Vector operations
let a = Vector3::new(1.0, 0.0, 0.0);
let b = Vector3::new(0.0, 1.0, 0.0);
let d = a.dot(b);
assert_eq!(d, 0.0, "orthogonal vectors have zero dot product");
let c = a.cross(b);
assert!((c.z - 1.0).abs() < 1e-6, "cross product of x and y should be z");
let len = a.normalize().length();
assert!((len - 1.0).abs() < 1e-6, "normalized vector has unit length");
// Matrix identity
let m = Matrix::identity();
// raylib's Matrix is row-major in memory; identity diagonal is m0/m5/m10/m15.
assert_eq!(m.m0, 1.0);
assert_eq!(m.m5, 1.0);
assert_eq!(m.m10, 1.0);
assert_eq!(m.m15, 1.0);
assert_eq!(m.m1, 0.0);
// Translation matrix
let t = Matrix::translate(3.0, 0.0, 0.0);
// m12 is the x-translation entry in raylib's row-major memory layout.
assert_eq!(t.m12, 3.0);
}
Gotchas
Quaternionis distinct fromVector4. In C,Quaternionis a typedef forVector4. raylib-rs uses a distinct#[repr(C)]struct with the same layout for type safety — the compiler will not let you pass aVector4where aQuaternionis expected. UseQuaternion::from(v4)/Vector4::from(q)for explicit zero-cost conversion.MintVec*types are deprecated. TheMintVec2/MintVec3/MintVec4/MintMatrix/MintQuattype aliases from 5.x are still present but carry#[deprecated(since = "6.0.0")]. Replace them with the nativeVector2/Vector3/Vector4/Matrix/Quaterniontypes. Seeecosystem/glam-mint-serde.mdfor the opt-in integration story.- Zero math-crate deps by default.
The default build depends only on raylib’s own raymath C implementation.
Enable
--features glam/--features mint/--features serdefor conversions/trait impls. Never assume those features are present in library code. - Row-major memory layout, semantic column-major ops.
raylib’s
Matrixis row-major in memory — the C source (raymath.h) writes translation intom12/m13/m14. The math operations and parameter naming, however, treat the matrix as column-major; the two conventions describe the same data via different access patterns.glam::Mat4is column-major in memory, so when comparing byte layouts side-by-side they will differ — but the--features glamadapter handles the mapping so values round-trip correctly.
See also
- 3D models —
Matrixtransforms used in model rendering. - Collision — uses
Vector2/Vector3/BoundingBox. - glam, mint, serde — opt-in conversions.
Vector3docs.rs /Matrixdocs.rs
Showcase examples
Showcase examples that exercise this module:
- shapes_math_angle_rotation
- shapes_math_sine_cosine
- shapes_vector_angle
- shapes_easings_testbed
- models_orthographic_projection
- models_yaw_pitch_roll
- models_tesseract_view
Collision
The collision module provides pure-data helpers for detecting overlap between
2D and 3D shapes. All functions are free functions (or methods on geometry
types) — no window, no GPU, no handle required. They map directly to raylib’s
CheckCollision* and GetRayCollision* family.
API surface
Rectangle::check_collision_recs— check overlap between twoRectangles.Rectangle::check_collision_point_rec— check if a point is inside a rectangle.Rectangle::check_collision_circle_rec— check if a circle overlaps a rectangle.Rectangle::get_collision_rec— compute the intersection rectangle of two colliding rectangles (Noneif they don’t overlap).check_collision_circles— check overlap between two circles.check_collision_point_circle— check if a point is inside a circle.check_collision_point_triangle— check if a point is inside a triangle.check_collision_point_line— check if a point is within a threshold distance of a line segment.check_collision_lines— check if two line segments intersect; returnsOption<Vector2>with the intersection point.check_collision_spheres— 3D sphere–sphere overlap check.BoundingBox::check_collision_boxes— 3D axis-aligned bounding box overlap check.
Example
Rectangle and circle collision — pure Rust, no window needed.
#![allow(unused)]
fn main() {
extern crate raylib;
use raylib::math::Rectangle;
use raylib::core::collision::{check_collision_circles, check_collision_lines};
use raylib::math::Vector2;
// Two overlapping rectangles
let r1 = Rectangle::new(0.0, 0.0, 10.0, 10.0);
let r2 = Rectangle::new(5.0, 5.0, 10.0, 10.0);
assert!(r1.check_collision_recs(r2), "overlapping rects should collide");
// A separated rectangle pair
let r3 = Rectangle::new(20.0, 20.0, 5.0, 5.0);
assert!(!r1.check_collision_recs(r3), "non-overlapping rects should not collide");
// Point inside rectangle
assert!(r1.check_collision_point_rec(Vector2::new(3.0, 3.0)));
assert!(!r1.check_collision_point_rec(Vector2::new(15.0, 15.0)));
// Two overlapping circles (centers 1 apart, radii sum = 4)
assert!(check_collision_circles(
Vector2::new(0.0, 0.0), 2.0,
Vector2::new(1.0, 0.0), 2.0,
));
// Line intersection
let hit = check_collision_lines(
Vector2::new(-1.0, -1.0), Vector2::new(1.0, 1.0),
Vector2::new(-1.0, 1.0), Vector2::new(1.0, -1.0),
);
assert!(hit.is_some(), "diagonal lines should intersect");
let pt = hit.unwrap();
assert!((pt.x).abs() < 1e-4 && (pt.y).abs() < 1e-4);
}
Gotchas
check_collision_recsis a method onRectangle, not a free function. Call it asrect.check_collision_recs(other). Similarly,check_collision_point_rec,check_collision_circle_rec, andget_collision_recare methods onRectangle.check_collision_linesreturnsOption<Vector2>, notbool. Use.is_some()to test for collision and.unwrap()to read the intersection point.- Touch counts as collision. Circles whose edges exactly touch (distance equals the sum of radii) are considered colliding; this matches raylib’s C behaviour.
See also
- raymath —
Vector2/Vector3/BoundingBoxtypes used here. - Shapes — drawing the same geometry you’re testing.
check_collision_recsdocs.rs
Showcase examples
Showcase examples that exercise this module:
raygui
raygui is an immediate-mode GUI toolkit
bundled with raylib. raylib-rs 6.0 exposes all 57 RAYGUIAPI functions at
parity with raygui 6.0. Controls are grouped into sub-traits
(RaylibGuiControls, RaylibGuiContainers, RaylibGuiState,
RaylibGuiAdvanced, RaylibGuiIcons) and blanket-implemented for every draw
handle — just use raylib::prelude::* and the methods are available inside
a begin_drawing frame.
String parameters accept impl AsRef<str> (pass &str, String, or any
type that derefs to a string slice) — conversion goes through a thread-local
scratch buffer so there are no per-frame heap allocations.
Feature gate. raygui is behind the
rayguiCargo feature. Addfeatures = ["raygui"](or"full") to yourCargo.tomldependency.
API surface
RaylibGuiControls::gui_button— button control; returnstruewhen clicked.RaylibGuiControls::gui_label— label control; displays text.RaylibGuiControls::gui_slider— horizontal slider; takes&mut f32for the current value.RaylibGuiControls::gui_combo_box— combo box; semicolon-separated option string,&mut i32active index.RaylibGuiContainers::gui_window_box— window box container; returnstruewhen the close button is clicked.RaylibGuiState::gui_set_state— set global GUI state (Normal,Focused,Pressed,Disabled).RaylibGuiState::gui_load_style— load a.rgsstyle file to retheme all controls.RaylibDrawGui— umbrella supertrait re-exported for source compatibility.
Example
raygui controls require an active drawing frame and a GPU context.
The example below is marked ignore because the software_renderer feature
is mutually exclusive with the default OpenGL build used by the book’s CI.
The WS9 showcase will provide a
runnable raygui demo once the web gallery is deployed.
extern crate raylib;
use raylib::prelude::*;
fn main() {
let (mut rl, thread) = raylib::init()
.size(400, 300)
.title("raygui demo")
.build();
let mut slider_val: f32 = 0.5;
while !rl.window_should_close() {
let mut d = rl.begin_drawing(&thread);
d.clear_background(Color::RAYWHITE);
// Label
d.gui_label(
Rectangle::new(20.0, 20.0, 200.0, 30.0),
"Hello, raygui!",
);
// Button — returns true on click
if d.gui_button(Rectangle::new(20.0, 60.0, 120.0, 30.0), "Click me") {
println!("button clicked");
}
// Slider — mutates slider_val in place
d.gui_slider(
Rectangle::new(20.0, 110.0, 200.0, 20.0),
"Min",
"Max",
&mut slider_val,
0.0,
1.0,
);
}
}
Gotchas
- String parameters are
impl AsRef<str>. Pass&str,String, or any type implementingAsRef<str>. Under the hood, each call writes the string to a thread-localCStringscratch buffer — safe and zero-alloc per frame. - GUI state is global. raygui is immediate-mode; all style and state
(
gui_set_state,gui_load_style) affects every control drawn this frame. Call ordering matters. gui_load_style_from_memoryis absent from the vendored raygui header (backlog #296 — deferred until the vendored raygui advances).rayguifeature required. Withoutfeatures = ["raygui"]the trait methods are not compiled in.
See also
- Window and drawing — the frame loop that hosts GUI calls.
- Strings and allocations — why raygui uses
impl AsRef<str>. RaylibGuiControlsdocs.rs
Showcase examples
Showcase examples that exercise this module:
rlgl
rlgl is raylib’s thin OpenGL abstraction layer — the layer below raylib’s
2D/3D rendering modules that maps draw calls to OpenGL 3.3, OpenGL ES 2.0, or
the software renderer depending on the active backend.
raylib-rs 6.0 exposes a safe wrapper for the immediate-mode subset of
rlgl.h: a matrix stack, immediate-mode vertex streams, render-state toggles,
and ergonomic methods that bind the crate’s safe [Texture2D] and [Shader]
handles. The full 161-function rlgl surface remains available as ffi
(power-user escape hatch) — the safe layer covers the everyday drawing needs.
All entry points hang off the [RaylibRlgl] trait, which is blanket-implemented
for every draw handle, so they’re only callable inside a begin_drawing frame.
API surface
RaylibRlgl::rl_push_matrix— push the current matrix; returns an [RlMatrix] guard that auto-pops on drop.RaylibRlgl::rl_translatef/rl_rotatef/rl_scalef— multiply the current matrix by a translation / rotation / scale.RaylibRlgl::rl_ortho— multiply the current matrix by an orthographic projection.RaylibRlgl::rl_begin— begin a vertex stream; returns an [RlImmediate] guard that ends it on drop.RaylibRlgl::rl_draw— closure form: begin a stream, callbody, end automatically.RaylibRlgl::rl_set_texture/rl_enable_texture/rl_disable_texture— bind/unbind a [Texture2D] for subsequent immediate-mode draws.RaylibRlgl::rl_enable_shader/rl_set_shader— bind a [Shader] by its safe handle.DrawMode—Lines,Triangles,Quads(maps toRL_LINES/RL_TRIANGLES/RL_QUADS).RlMatrix/RlImmediate— RAII guards for the matrix stack and vertex stream.
Example
The example requires a live GPU context and so cannot run in the book’s CI
build (the software_renderer feature is mutually exclusive with the default
OpenGL build). The WS9 showcase will
provide a runnable demo.
extern crate raylib;
use raylib::prelude::*;
use raylib::rlgl::{DrawMode, RaylibRlgl};
fn main() {
let (mut rl, thread) = raylib::init()
.size(640, 480)
.title("rlgl demo")
.build();
while !rl.window_should_close() {
let mut d = rl.begin_drawing(&thread);
d.clear_background(Color::RAYWHITE);
// Push a matrix, translate, draw an immediate-mode triangle, pop.
{
let mut m = d.rl_push_matrix();
m.rl_translatef(320.0, 240.0, 0.0);
m.rl_draw(DrawMode::Triangles, |v| {
v.color4ub(Color::RED);
v.vertex2f(-50.0, -50.0);
v.color4ub(Color::GREEN);
v.vertex2f( 50.0, -50.0);
v.color4ub(Color::BLUE);
v.vertex2f( 0.0, 50.0);
});
} // RlMatrix drops here → rlPopMatrix called automatically
}
}
Gotchas
RlMatrixauto-pops on drop. Do not callrl_pop_matrixmanually after usingrl_push_matrix— the RAII guard handles it. The guard derefs to the parent draw handle so you can call other draw methods through it.- Immediate-mode vertices need texcoords under the software renderer.
The rlsw backend samples the shapes texture; without
texcoord2fcalls the default(0,0)coordinate hits a transparent texel and emits zero pixels. Always emittexcoord2falongsidevertex2f/vertex3fwhen testing with the software renderer. - GL-object lifecycle stays with RAII.
rl_set_texture/rl_enable_shaderbind the existing RAII handles; they do not create or destroy GPU objects. Lifecycle management remains withTexture2D::Drop/Shader::Dropand the rawfficreate/destroy functions.
See also
- Window and drawing — the draw handle that hosts rlgl calls.
- Software renderer — the headless backend used in render tests.
RaylibRlgldocs.rs
Showcase examples
Showcase examples that exercise this module:
- rlgl_standalone — desktop only
- shapes_rlgl_color_wheel
- shapes_rlgl_triangle
- models_rlgl_solar_system
Software renderer
raylib 6.0 adds rlsw, a software renderer backend that renders into an
in-memory framebuffer with no GPU or window required. raylib-rs exposes it
via the software_renderer Cargo feature and the test_harness module
(source)
— a set of helpers that initialise a windowless context, draw a frame,
read the framebuffer back, and let you probe pixel values. Note: test_harness
is gated on #[cfg(feature = "software_renderer")] and does not appear on
docs.rs (docs.rs builds with only the nobuild feature).
This is the mechanism behind raylib-rs’s Tier-2 render tests: the CI
software-render job runs on all three platforms (ubuntu/macOS/windows)
without a display server.
API surface
software_rendererCargo feature — enables the rlsw backend. Addfeatures = ["software_renderer"]to yourCargo.tomldependency.with_headless(w, h, body)— initialise aw × hwindowless context, runbody(rl, thread), then tear down. Call at most once per test process (raylib is single-init per process).render_frame(rl, thread, draw)— draw one frame via thedrawclosure and return a normalized top-left RGBAImage. Coordinates and colors match what you drew:Color::REDat screen(x, y)reads back as red at(x, y).render_frame_raw(rl, thread, draw)— raw readback: BGRA bytes, Y-inverted. Use only when you need the unprocessed rlsw output.pixel_at(img, x, y)— read the pixel at(x, y)as aPxvalue withr/g/b/afields.assert_pixel(img, x, y, expected, tol)— assert the RGB channels at(x, y)matchexpectedwithin a per-channel tolerancetol. Alpha is intentionally ignored (the Memory platform readback does not guarantee alpha fidelity).
Example
The example below is rust,ignore because raylib’s CI build of the rlib that
backs mdbook test uses the full feature, which falls through to
PLATFORM=Desktop with the OpenGL backend. test_harness lives under
#[cfg(feature = "software_renderer")], so it isn’t compiled into that rlib.
To actually run a software-renderer test, compile the crate with
--no-default-features --features software_renderer,SUPPORT_MODULE_RTEXTURES,SUPPORT_MODULE_RSHAPES,SUPPORT_MODULE_RTEXT,SUPPORT_MODULE_RMODELS,SUPPORT_IMAGE_GENERATION,raygui
(this is what check.yml/test.yml do for the Tier-2 render tests). The WS9
showcase pipeline is the eventual home for runnable demos.
extern crate raylib;
use raylib::prelude::*;
use raylib::test_harness::{with_headless, render_frame, assert_pixel};
#[test]
fn red_rectangle_center_pixel() {
with_headless(256, 256, |rl, thread| {
let img = render_frame(rl, thread, |d| {
d.clear_background(Color::BLACK);
d.draw_rectangle(64, 64, 128, 128, Color::RED);
});
// Center of the rectangle should be red.
assert_pixel(&img, 128, 128, Color::RED, 0);
// A corner well outside the rectangle should be black.
assert_pixel(&img, 4, 4, Color::BLACK, 0);
});
}
Gotchas
software_rendereris not enabled by thefullfeature alias. Thefullalias explicitly excludes mutually-exclusive backend selectors (opengl_*,sdl,wayland,drm,software_renderer) perraylib/Cargo.toml. A--features fullbuild therefore defaults toPLATFORM=Desktopwith OpenGL —test_harnesswon’t be compiled in. To use the software renderer, build with--no-default-features --features software_renderer,...(see the canonical command below). Thecompile_error!inraylib-sys/build.rsonly fires when an explicitopengl_*(ordrm) feature is combined withsoftware_renderer;fullalone does not trigger it.software_rendereris mutually exclusive withwasm32-unknown-emscripten(tracked-deferred). rlsw-on-Emscripten is not yet supported; seedocs/superpowers/notes/ws6b-complete.md.- All required raylib modules must be linked.
The harness requires
SUPPORT_MODULE_RSHAPES,SUPPORT_MODULE_RTEXTURES,SUPPORT_MODULE_RTEXT,SUPPORT_MODULE_RMODELS,SUPPORT_MODULE_RAUDIO, plusSUPPORT_IMAGE_GENERATION(the safegen_image_*family is currently ungated; MSVC link fails without it). Therayguifeature is also enabled in CI sorender_guitests link cleanly. Disabling any required module causes a link error; see the memory notesoftware-renderer-headless-testing. with_headlessis single-init. raylib can only be initialised once per process. Run software-renderer tests with:cargo test -p raylib --no-default-features \ --features software_renderer,SUPPORT_MODULE_RTEXTURES,SUPPORT_MODULE_RSHAPES,SUPPORT_MODULE_RTEXT,SUPPORT_MODULE_RMODELS,SUPPORT_IMAGE_GENERATION,raygui \ -- --test-threads=1--test-threads=1is required because the harness initialises raylib’s platform layer once per process. In a test suite, wrap all headless tests inside a singlewith_headlesscall or use a process-global init strategy (e.g.,std::sync::OnceLock).
See also
- Features and platforms — feature flag reference.
test_harnesssource — module is cfg-gated onsoftware_renderer; not surfaced on docs.rs.docs/superpowers/notes/ws4b-complete.md— WS4b harness design rationale.docs/superpowers/notes/ws5-complete.md— readback normalization details.
Callbacks and logging
raylib exposes hooks for several behaviours: trace-log output, custom file I/O loaders/savers, and audio stream processing. raylib-rs wraps the trace-log hook as a plain Rust function pointer so you can redirect raylib’s diagnostic output to your own logger.
The logging free functions (trace_log, set_trace_log) live in
raylib::core::logging and are re-exported through raylib::prelude.
API surface
set_trace_log_callback— install a callbackfn(TraceLogLevel, &str)that receives every raylib log message. ReturnsOk(())immediately; the old callback is silently replaced.TraceLogLevel— enum:LOG_ALL,LOG_TRACE,LOG_DEBUG,LOG_INFO,LOG_WARNING,LOG_ERROR,LOG_FATAL,LOG_NONE.set_trace_log— set the minimum log level; messages below it are suppressed.trace_log— emit a log message at the given level.set_save_file_data_callback/set_load_file_data_callback/set_save_file_text_callback/set_load_file_text_callback— replace raylib’s file I/O with your own Rust closures (e.g., to read assets from an archive).attach_audio_stream_processor_to_music— attach anFnMut(&mut [f32], u32)audio processor to aMusicstream; returns a pinned guard that detaches on drop.
Example
Install a trace-log callback and emit one message. No window needed to install
the callback, but trace_log calls into ffi::TraceLog which does require an
active raylib context, so this example is no_run.
extern crate raylib;
use raylib::core::callbacks::set_trace_log_callback;
use raylib::core::logging::trace_log;
use raylib::consts::TraceLogLevel;
fn my_logger(level: TraceLogLevel, msg: &str) {
eprintln!("[{level:?}] {msg}");
}
fn main() {
// Install the callback before init so we capture startup messages too.
set_trace_log_callback(my_logger).expect("callback already set");
let (_rl, _thread) = raylib::init()
.size(1, 1)
.title("callback demo")
.build();
// Emit a custom message through the same callback.
trace_log(TraceLogLevel::LOG_INFO, "Hello from the custom logger");
}
Gotchas
- Callbacks are global state. Installing a new trace-log callback replaces
the previous one.
set_trace_log_callbackalways returnsOk(()). The old methods onRaylibHandle(e.g.,rl.set_trace_log_callback(...)) are#[deprecated]— use the free function form instead. - The audio callback was removed in 6.0. The old per-sample audio callback
API is gone entirely. Use
AudioStreamwithis_processed/update_audio_streamfor custom PCM generation, or attach a processor viaattach_audio_stream_processor_to_music. - File I/O callbacks replace raylib’s I/O for the lifetime of the process.
There is no “unset” for
set_load_file_data_callbacketc. — they can only be set once per callback slot.
Routing raylib logs through the log crate
With the opt-in log feature, the window builder can forward raylib’s
TraceLog output into the log facade, so your
application’s logger (env_logger, tracing-log, …) receives raylib’s
logs alongside your own:
[dependencies]
raylib = { version = "6", features = ["log"] }
log = "0.4"
env_logger = "0.11"
env_logger::init(); // any `log`-compatible logger
let (mut rl, thread) = raylib::init()
.size(800, 450)
.title("game")
.log_to_rust()
.build();
log::info!("app and raylib logs share one pipeline now");
Messages arrive under the target "raylib", so RUST_LOG=raylib=debug
scopes raylib’s output independently of your app’s.
The bridge makes the log facade the single level filter: it sets
raylib’s own threshold to LOG_ALL (overriding .log_level()), so what
you see is controlled entirely by your logger’s configuration. Raylib
levels map TRACE→trace, DEBUG→debug, INFO→info, WARNING→warn,
and both ERROR and FATAL to error (the facade has no fatal level;
raylib still aborts after a fatal log, unchanged).
Two caveats:
- The bridge claims the same single callback slot as
set_trace_log_callback— they are mutually exclusive, last writer wins. - The bridge only emits into the facade. Without a logger installed,
the messages are silently dropped, like any library using
log. (It also requires theSUPPORT_TRACELOGfeature, which is in the default set.)
See also
- Audio —
AudioStreamand the audio processor callback. set_trace_log_callbackdocs.rs
Showcase examples
Showcase examples that exercise this module:
Error handling
raylib-rs uses structured error types for all fallible operations. The safe
crate defines a rich set of typed errors in
raylib::core::error —
each domain (images, textures, fonts, audio, models, etc.) has its own error
enum. A top-level
RaylibError
aggregates all of them via #[from] conversions for callers that don’t need to
distinguish the source.
Drawing operations are infallible — there is no error path for
draw_rectangle, draw_text, etc. Errors arise only at resource load time.
API surface
InvalidImageError— returned byImage::load_image,Image::load_image_from_mem, etc. Variants includeNullDataFromFile(file not found / unsupported format),ZeroWidth/ZeroHeight,UnsupportedFormat.LoadTextureError— returned byload_texture,load_texture_from_image,load_render_texture.LoadFontError— returned byload_font,load_font_ex.LoadSoundError— returned byRaylibAudio::new_sound,new_wave,new_music.LoadModelError— returned byload_model,load_model_from_mesh.RaylibError— top-level enum aggregating all domain errors viaFromimpls; useful when propagating with?through a function that loads multiple resource types.raylib::init().build()panics on a second call (window context is process-global). All other APIs areResult-based.
Example
extern crate raylib;
use raylib::core::texture::Image;
use raylib::core::error::InvalidImageError;
fn main() {
match Image::load_image("nonexistent.png") {
Ok(img) => {
println!("loaded {}x{} image", img.width(), img.height());
}
Err(InvalidImageError::NullDataFromFile) => {
eprintln!("file not found or unsupported format");
}
Err(e) => {
eprintln!("unexpected image error: {e}");
}
}
}
Gotchas
- Error variants are typed, not stringly-typed.
Unlike some thin C-binding layers, raylib-rs maps sentinel return values
to named enum variants. This makes
matchexhaustive and lets you distinguish “file not found” from “GPU upload failed” without parsing a message string. - Errors use
thiserror. Every error type implementsstd::error::ErrorandDisplay. They compose naturally withanyhow,eyre, or anyBox<dyn Error>handler. ?propagation works if you useRaylibErroras your function’s error type — every domain error has aFromimpl into it. Alternatively, the individual domain errors can be mapped with.map_err(RaylibError::from).raylib::init().build()panics, notErr. Double-init is a programming error (not a runtime condition), so it panics rather than returningErr.
See also
- Safety — how the safe wrapper prevents UB.
- Textures and images —
ImageandTexture2Dload paths. RaylibErrordocs.rs
glam, mint, serde
raylib-rs 6.0 makes math-ecosystem integration strictly opt-in. The
default build ships zero extra math-crate dependencies — native #[repr(C)]
types (Vector2, Vector3, Vector4, Matrix, Quaternion) cover every
API surface with no third-party crate required.
Enable glam, mint, or serde as Cargo features to unlock From/Into
conversions and serialization trait derives on those native types.
API surface
--features glam— addsFrom/Intobetween:Vector2↔glam::Vec2Vector3↔glam::Vec3Vector4↔glam::Vec4Quaternion↔glam::QuatMatrix↔glam::Mat4(column-major round-trip; see Gotchas)
--features mint— addsFrom/Intobetween:Vector2↔mint::Vector2<f32>Vector3↔mint::Vector3<f32>Vector4↔mint::Vector4<f32>Quaternion↔mint::Quaternion<f32>Matrix↔mint::ColumnMatrix4<f32>
--features serde— addsSerialize/Deserializederives on the math types (Vector2/Vector3/Vector4/Matrix/Quaternion) and onColor.
Example
full includes glam, so this doctest runs under cargo test --features full
and under mdbook test -L target/debug/deps.
#![allow(unused)]
fn main() {
extern crate raylib;
extern crate glam;
use raylib::math::Vector3;
// Convert to glam for library interop, then convert back.
let rl_v = Vector3::new(1.0, 2.0, 3.0);
let gv: glam::Vec3 = rl_v.into();
assert_eq!(gv, glam::Vec3::new(1.0, 2.0, 3.0));
let round_trip: Vector3 = gv.into();
assert_eq!(round_trip.x, rl_v.x);
assert_eq!(round_trip.y, rl_v.y);
assert_eq!(round_trip.z, rl_v.z);
}
Gotchas
MintVec*/MintMatrix/MintQuatare deprecated. TheMintVec2,MintVec3,MintVec4,MintMatrix, andMintQuattype aliases from the 5.x era are still present but carry#[deprecated(since = "6.0.0")]. Replace them with the native types (Vector2,Vector3,Vector4,Matrix,Quaternion) and, if you need mint interop, enable--features mint.glam::Mat4column-major vs. raylibMatrixrow-major. raylib’sMatrixis row-major in memory (translation lives inm12/m13/m14; see raymath chapter).glam::Mat4is column-major in memory. The--features glamadapter handles the field permutation so values round-trip correctly — but if you inspect raw byte layouts side-by-side,m0..m15will differ fromglam’s internal storage order.- Zero math-crate deps by default.
Library crates built on raylib-rs should not enable
glam/mint/serdein their own[features]defaults unless they genuinely need them — doing so forces the dependency onto every downstream consumer.
See also
- raymath — the native math types and their methods.
- Features and platforms — the full feature flag reference.
- docs.rs feature-gated items — use the Feature flags section on docs.rs to browse the glam/mint/serde items.
What next?
You’ve read through the getting-started guides, the core concepts, the module chapters, and the ecosystem integrations. Here’s where to go from here.
Pointers
-
docs.rs API reference — the full rustdoc for every public type, function, and trait in
raylib. Use the search bar and the Feature flags panel to browse feature-gated items (glam,mint,serde,raygui, …). -
Upstream raylib — the C library reference, the official cheatsheet, examples, and a community forum. When a function isn’t documented in the Rust side, the C docs are the authoritative source.
-
The showcase — WS9 (the 6.0 finale) will port the full set of official raylib examples to Rust and deploy them as a GitHub Pages gallery. The portfolio lives under
showcase/in the repository today; the hosted gallery will appear when WS9 ships. -
The repository — source code, issue tracker, and the
docs/superpowers/directory which holds the spec and plan archive for the entire 6.0 upgrade effort. -
CONTRIBUTE.md — contribution guide covering
unsafeconventions, RAII expectations, the# Safety/// SAFETY:comment discipline, and the PR process.
See also
- Introduction — the book’s opening page.
- Install on Windows, macOS, Linux, Web — platform-specific setup guides.
Showcase examples
This is the full inventory of the 229 examples in the raylib-rs showcase
(217 raylib core + 12 raygui), grouped by category. Each entry links to
the example’s WASM-rendered page (or a desktop-only placeholder if not
wasm-buildable).
Live gallery: https://raylib-rs.github.io/raylib-rs/
Audio (11)
- audio_amp_envelope
- audio_mixed_processor
- audio_module_playing
- audio_music_stream
- audio_raw_stream
- audio_sound_loading
- audio_sound_multi
- audio_sound_positioning
- audio_spectrum_visualizer — desktop only
- audio_stream_callback
- audio_stream_effects
Core (49)
- core_2d_camera
- core_2d_camera_mouse_zoom
- core_2d_camera_platformer
- core_2d_camera_split_screen
- core_3d_camera_first_person
- core_3d_camera_fps
- core_3d_camera_free
- core_3d_camera_mode
- core_3d_camera_split_screen
- core_3d_picking
- core_automation_events — desktop only
- core_basic_screen_manager
- core_basic_window
- core_clipboard_text
- core_compute_hash
- core_custom_frame_control — desktop only
- core_custom_logging
- core_delta_time
- core_directory_files
- core_drop_files — desktop only
- core_highdpi_demo
- core_highdpi_testbed
- core_input_actions
- core_input_gamepad
- core_input_gestures
- core_input_gestures_testbed
- core_input_keys
- core_input_mouse
- core_input_mouse_wheel
- core_input_multitouch
- core_input_virtual_controls
- core_keyboard_testbed
- core_monitor_detector
- core_random_sequence
- core_random_values
- core_render_texture
- core_scissor_test
- core_screen_recording — desktop only
- core_smooth_pixelperfect
- core_storage_values
- core_text_file_loading
- core_undo_redo
- core_viewport_scaling
- core_vr_simulator — desktop only
- core_window_flags
- core_window_letterbox
- core_window_should_close
- core_window_web
- core_world_screen
Models (30)
- models_animation_blend_custom — desktop only
- models_animation_blending — desktop only
- models_animation_gpu_skinning — desktop only
- models_animation_timing
- models_basic_voxel
- models_billboard_rendering
- models_bone_socket
- models_box_collisions
- models_cubicmap_rendering
- models_decals
- models_directional_billboard
- models_first_person_maze
- models_geometric_shapes
- models_heightmap_rendering
- models_loading
- models_loading_gltf
- models_loading_iqm
- models_loading_m3d
- models_loading_vox — desktop only
- models_mesh_generation
- models_mesh_picking
- models_orthographic_projection
- models_point_rendering
- models_rlgl_solar_system
- models_rotating_cube
- models_skybox_rendering — desktop only
- models_tesseract_view
- models_textured_cube
- models_waving_cubes
- models_yaw_pitch_roll
Others (3)
- embedded_files_loading
- raylib_opengl_interop — desktop only
- rlgl_standalone — desktop only
raygui (12)
- animation_curve
- controls_test_suite
- custom_file_dialog — desktop only
- custom_input_box
- custom_sliders
- floating_window
- image_exporter
- image_importer_raw — desktop only
- portable_window — desktop only
- property_list
- scroll_panel
- style_selector
Shaders (35)
- shaders_ascii_rendering
- shaders_basic_lighting
- shaders_basic_pbr
- shaders_cel_shading
- shaders_color_correction
- shaders_custom_uniform
- shaders_deferred_rendering — desktop only
- shaders_depth_rendering — desktop only
- shaders_depth_writing — desktop only
- shaders_eratosthenes_sieve
- shaders_fog_rendering
- shaders_game_of_life
- shaders_hot_reloading — desktop only
- shaders_hybrid_rendering — desktop only
- shaders_julia_set
- shaders_lightmap_rendering
- shaders_mandelbrot_set
- shaders_mesh_instancing
- shaders_model_shader
- shaders_multi_sample2d
- shaders_normalmap_rendering
- shaders_palette_switch
- shaders_postprocessing
- shaders_raymarching_rendering
- shaders_rlgl_compute — desktop only
- shaders_rounded_rectangle
- shaders_shadowmap_rendering — desktop only
- shaders_shapes_textures
- shaders_simple_mask
- shaders_spotlight_rendering
- shaders_texture_outline
- shaders_texture_rendering
- shaders_texture_tiling
- shaders_texture_waves
- shaders_vertex_displacement
Shapes (41)
- shapes_ball_physics
- shapes_basic_shapes
- shapes_bouncing_ball
- shapes_bullet_hell
- shapes_circle_sector_drawing
- shapes_clock_of_clocks
- shapes_collision_area
- shapes_colors_palette
- shapes_dashed_line
- shapes_digital_clock
- shapes_double_pendulum
- shapes_easings_ball
- shapes_easings_box
- shapes_easings_rectangles
- shapes_easings_testbed
- shapes_ellipse_collision
- shapes_following_eyes
- shapes_hilbert_curve
- shapes_kaleidoscope
- shapes_lines_bezier
- shapes_lines_drawing
- shapes_logo_raylib
- shapes_logo_raylib_anim
- shapes_math_angle_rotation
- shapes_math_sine_cosine
- shapes_mouse_trail
- shapes_penrose_tile
- shapes_pie_chart
- shapes_rectangle_advanced
- shapes_rectangle_scaling
- shapes_recursive_tree
- shapes_ring_drawing
- shapes_rlgl_color_wheel
- shapes_rlgl_triangle
- shapes_rounded_rectangle_drawing
- shapes_simple_particles
- shapes_splines_drawing
- shapes_starfield_effect
- shapes_top_down_lights
- shapes_triangle_strip
- shapes_vector_angle
Text (16)
- text_3d_drawing
- text_codepoints_loading
- text_font_filters
- text_font_loading
- text_font_sdf
- text_font_spritefont
- text_format_text
- text_inline_styling
- text_input_box
- text_rectangle_bounds
- text_sprite_fonts
- text_strings_management
- text_unicode_emojis
- text_unicode_ranges
- text_words_alignment
- text_writing_anim
Textures (32)
- textures_background_scrolling
- textures_blend_modes
- textures_bunnymark
- textures_cellular_automata
- textures_clipboard_image — desktop only
- textures_fog_of_war
- textures_framebuffer_rendering
- textures_gif_player
- textures_image_channel
- textures_image_drawing
- textures_image_generation
- textures_image_kernel
- textures_image_loading
- textures_image_processing
- textures_image_rotate
- textures_image_text
- textures_logo_raylib
- textures_magnifying_glass
- textures_mouse_painting
- textures_npatch_drawing
- textures_particles_blending
- textures_polygon_drawing
- textures_raw_data
- textures_screen_buffer
- textures_sprite_animation
- textures_sprite_button
- textures_sprite_explosion
- textures_sprite_stacking
- textures_srcrec_dstrec
- textures_textured_curve
- textures_tiled_drawing
- textures_to_image