sola-raylib 6.1.0

Safe Rust bindings for raylib. sola-raylib N.x tracks raylib N.x (e.g. 5.x → raylib 5.5; 6.x → raylib 6.0).
Documentation

sola-raylib

sola-raylib is an actively maintained Rust bindings and wrapper for raylib 6.0. It currently targets Rust toolchain version 1.78 or higher.

Versioning: sola-raylib's major version tracks raylib's major version — 5.x binds raylib 5.5, 6.x binds raylib 6.0, and so on. Minor and patch numbers are sola-raylib's own (raylib doesn't follow strict semver, so this project doesn't try to mirror it beyond the major).

This project is a fork of github.com/raylib-rs/raylib-rs from commit 91bcb492c61dc067945d59357ca6def0d83fcb2c (v5.5.1 release), which was a fork of github.com/deltaphc/raylib-rs.

Check out the examples directory to find usage examples. See CHANGELOG.md for the 6.x changes, including breaking signature changes and the new APIs wrapped from raylib 6.0.

sola-raylib development happens on main. This README.md covers what's in main. Be sure to view the tag version of the repository if you want to find details on a specific version.

The latest released version on crates.io is 6.1.0 (binds raylib 6.0).

Pull from GitHub if you want the latest main:

raylib = { package = "sola-raylib", git = "https://github.com/brettchalupa/sola-raylib.git" }

Features / Bugs

Though this binding tries to stay close to the simple C API, it makes some changes to be more idiomatic for Rust.

  • Resources are automatically cleaned up when they go out of scope (or when std::mem::drop is called). This is essentially RAII. This means that "Unload" functions are not exposed (and not necessary unless you obtain a Weak resource using make_weak()).
  • Most of the Raylib API is exposed through RaylibHandle, which is for enforcing that Raylib is only initialized once, and for making sure the window is closed properly. RaylibHandle has no size and goes away at compile time. Because of mutability rules, Raylib-rs is thread safe!
  • A RaylibHandle and RaylibThread are obtained through through the init() function which will allow you to build up some window options before initialization (replaces set_config_flags). RaylibThread should not be sent to any other threads, or used in a any syncronization primitives (Mutex, Arc) etc.
  • Manually closing the window is unnecessary, because CloseWindow is automatically called when RaylibHandle goes out of scope.
  • Model::set_material, Material::set_shader, and MaterialMap::set_texture methods were added since one cannot set the fields directly. Also enforces correct ownership semantics.
  • Font::from_data, Font::set_chars, and Font::set_texture methods were added to create a Font from loaded CharInfo data.
  • SubText and FormatText are omitted, and are instead covered by Rust's string slicing and Rust's format! macro, respectively.

Why use Rust with Raylib instead of C?

There are many benefits to coding games with Raylib using Rust instead of C:

  • Memory safety guarantees from the Rust compiler in the code you write.
  • Best-in-class developer experience with cargo and Rust's language server protocol (LSP).
  • Lots of great Rust packages for game development and systems programming on crates.io.
  • Easier cross-platform compiling.

Installation

Supported Platforms

sola-raylib is focused on supporting Windows, Linux, macOS, and Web targets.

The table below shows which core APIs are supported for which platforms:

API Windows Linux macOS Web
core :heavy_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark:
rgui :heavy_check_mark: :heavy_check_mark: :heavy_check_mark:
rlgl :heavy_check_mark: :x: :x:

Build Dependencies

Requires glfw, cmake, and curl. Tips on making things work smoothly on all platforms is appreciated. Follow instructions for building raylib for your platform here

  1. Add the dependency to your Cargo.toml:
[dependencies]
sola-raylib = "6.0"

Then in your code, use it as sola_raylib:

use sola_raylib::prelude::*;

Drop-in replacement for raylib-rs

If you're migrating an existing raylib-rs project and don't want to touch every use raylib::... statement, use Cargo's package rename so the crate is still imported as raylib in your source code:

[dependencies]
raylib = { package = "sola-raylib", version = "6.0" }

With that line, all your existing raylib code keeps working. The ./examples in this repository use this style.

  1. Start coding!
use sola_raylib::prelude::*;

fn main() {
    let (mut rl, thread) = sola_raylib::init()
        .size(640, 480)
        .title("Hello, World")
        .build();

    while !rl.window_should_close() {
        let mut d = rl.begin_drawing(&thread);

        d.clear_background(Color::WHITE);
        d.draw_text("Hello, world!", 12, 12, 20, Color::BLACK);
    }
}

Building for the web (wasm)

sola-raylib targets wasm32-unknown-emscripten so games can run in the browser. The link flags raylib needs (-sUSE_GLFW=3, -sASYNCIFY=1, -sFORCE_FILESYSTEM=1, -sSUPPORT_LONGJMP=wasm, -sEXPORTED_RUNTIME_METHODS=..., -sALLOW_MEMORY_GROWTH=1) live in your project's .cargo/config.toml: cargo silently drops cargo:rustc-link-arg from rlib build scripts, so a sys crate can't inject them. raylib's own C compiles cleanly under rustc 1.93+'s default link ABI; no sys-side cflag tweaks are needed.

There is also game_loop::run, a cross-platform game-loop helper that registers a per-frame closure with emscripten_set_main_loop_arg on the web and drives a normal while-loop natively. Same source for both targets.

The full recipe (asset bundling, save data, audio, deploy) lives in book/src/web.md. Reference example: examples/hello_raylib.rs; copyable config: examples/.cargo/config.toml. just build-web then just serve-web opens it at http://localhost:3535.

Cross-compiling using cross

Cross compiling with sola-raylib can be made easier with cross. See the upstream raylib-rs wiki for a writeup that should still largely apply.

Tech Notes

  • Structs holding resources have RAII/move semantics, including: Image, Texture2D, RenderTexture2D, Font, Mesh, Shader, Material, and Model.
  • Wave, Sound, Music, and AudioStream have lifetimes bound to AudioHandle.
  • Functions dealing with string data take in &str and/or return an owned String, for the sake of safety. The exception to this is the gui draw functions which take &CStr to avoid per frame allocations. The rstr! macro helps make this easy.
  • In C, LoadFontData returns a pointer to a heap-allocated array of CharInfo structs. In this Rust binding, said array is copied into an owned Vec<CharInfo>, the original data is freed, and the owned Vec is returned.
  • In C, LoadDroppedFiles returns a pointer to an array of strings owned by raylib. Again, for safety and also ease of use, this binding copies said array into a Vec<String> which is returned to the caller.
  • Linking is automatic, though I've only tested on Windows 10, Ubuntu, and MacOS 15. Other platforms may have other considerations.
  • OpenGL 3.3, 2.1, and ES 2.0 may be forced via adding ["opengl_33"], ["opengl_21"] or ["opengl_es_20] to the features array in your Cargo.toml dependency definition.

Cargo features

Every Cargo feature exposed by sola-raylib is listed here. The safe crate forwards each name through to sola-raylib-sys, so you can enable them on either crate and the effect is the same.

Build / linking

Feature Default Effect
bindgen yes Run bindgen at build time to produce FFI bindings. Disable to supply a hand-rolled bindings.rs (for platforms bindgen can't target).
nobuild no Skip compiling and linking raylib entirely. For docs.rs and headless setups; you are then responsible for linking raylib yourself.

Platform & rendering backend

Feature Default Effect
wayland no Build raylib's GLFW with native Wayland support on Linux. Requires the system glfw-devel (CMake config files), not just the runtime; see "Building for Wayland on Linux" below. Without this feature, GLFW uses X11/XWayland.
sdl no Use the SDL platform backend instead of GLFW. Build script auto-picks SDL3 if present, else SDL2, via pkg-config. Install with sudo dnf install SDL2-devel (Fedora), sudo apt install libsdl2-dev (Debian/Ubuntu), brew install sdl2 (macOS).
opengl_33 no Force OpenGL 3.3 backend.
opengl_21 no Force OpenGL 2.1 backend.
opengl_es_20 no Force OpenGL ES 2.0 backend.
opengl_es_30 no Force OpenGL ES 3.0 backend.
software_render no Experimental; see "Experimental raylib 6.0 platform flags".
platform_memory no Experimental; see "Experimental raylib 6.0 platform flags".
platform_web_rgfw no Experimental; see "Experimental raylib 6.0 platform flags".

Behavior toggles

Feature Default Effect
custom_frame_control no Enable raylib's SUPPORT_CUSTOM_FRAME_CONTROL so you drive frame timing yourself (call SwapScreenBuffer, PollInputEvents manually).
noscreenshot no Disable raylib's built-in F12-screenshot keybind. Implemented by pre-defining SUPPORT_SCREEN_CAPTURE=0 directly on the C compile line, leaving the rest of raylib's SUPPORT_* defaults intact. (Earlier 6.0 builds routed this through CMake's CUSTOMIZE_BUILD switch and broke rendering on Linux; see issue #40, fixed in 6.1.)

Interop

Feature Default Effect
with_serde no Derive serde::Serialize / Deserialize on the public types via serde + serde_json.
convert_mint no Implement conversions to/from mint math types.

Removed in 6.1: the nogif feature is gone. Raylib 6.0 dropped its built-in GIF recorder upstream, so nogif had no effect, just the harmful side-effect of triggering the same CUSTOMIZE_BUILD regression as noscreenshot. If you previously listed it in your Cargo.toml, just remove it.

Platform-Specific Notes

Windows

On Windows, your compiled binary will show a log window by default. If you want to hide this, add the following to your main.rs:

#![windows_subsystem = "windows"]

Experimental raylib 6.0 platform flags

sola-raylib 6.0 exposes three feature flags for raylib 6.0's new backends. All three are experimental upstream, shipped with known gaps in raylib 6.0 itself. We surface them for opt-in use, but what actually renders or links is whatever raylib's C side supports at HEAD. Expect rough edges.

  • software_render: compiles raylib with the CPU rlsw software rasterizer. Must be combined with the sdl feature on Linux/macOS because rlsw is not compatible with raylib's default GLFW desktop backend (confirmed upstream in raylib#5664; enabling only software_render produces a black window). The sdl feature needs SDL dev headers installed at build time; raylib prefers SDL3 and falls back to SDL2, and our build.rs auto-detects which is present via pkg-config. Install with sudo dnf install SDL2-devel on Fedora, sudo apt install libsdl2-dev on Debian/Ubuntu, brew install sdl2 on macOS. Typical usage:

    # Linux or macOS, with SDL installed:
    cargo add sola-raylib --features sdl,software_render
    

    Or to try it from this repo: just example-sw hello_raylib.

    Windows would need the rcore_desktop_win32 native backend (not yet wired in sola-raylib).

  • platform_memory: compiles the PLATFORM=Memory headless framebuffer backend. The backend builds and links; the APIs for reading the framebuffer back out (from rlsw.h, e.g. swGetColorBuffer) are not yet wrapped in the safe crate, so there's no usable headless-capture path today.

  • platform_web_rgfw: swaps the Emscripten/GLFW web backend for RGFW (PLATFORM=WebRGFW) when cross-compiling to wasm32-unknown-emscripten. Only meaningful with an emscripten build loop.

If you're evaluating one of these for production, test on your target platform first and expect to track upstream raylib for fixes.

Drop ordering

Resources like Texture2D, RenderTexture2D, Font, Model, Mesh, and Shader hold GPU handles and free them in their Drop impl. RaylibHandle's Drop calls CloseWindow(), which tears down the GL context. GPU resources must drop before the RaylibHandle — otherwise their unload calls run against a dead context and segfault.

Rust drops local variables in reverse declaration order, and struct fields in declaration order. So if you hold both resources and RaylibHandle in the same struct, declare rl last:

struct Engine {
    // resources (dropped first, while the GL context is still alive)
    texture: Texture2D,
    rt: RenderTexture2D,
    // handle (dropped last -> CloseWindow runs after resources are unloaded)
    thread: RaylibThread,
    rl: RaylibHandle,
}

The same rule applies when rl and resources are locals in the same function: declare rl first so it drops last.

Audio resources (Wave, Sound, Music, AudioStream) are lifetime-bound to RaylibAudio, so the borrow checker enforces their ordering for you — no discipline required.

Building from source

  1. Clone repository: git clone --recurse-submodules
  2. cargo build

If building for Wayland on Linux

  1. Install these packages:
    libglfw3-dev wayland-devel libxkbcommon-devel wayland-protocols wayland-protocols-devel libecm-dev
  2. Enable wayland by adding features=["wayland"] to your dependency definition

Note that the packages may not be a comprehensive list, please add details for your distribution or expand on these packages if you believe this to be incomplete.

Extras

  • In addition to the base library, there is also a convenient ease module which contains various interpolation/easing functions ported from raylib's easings.h, as well as a Tween struct to assist in using these functions.
  • Equivalent math and vector operations, ported from raymath.h, are impled on the various Vector and Matrix types. Operator overloading is used for more intuitive design.

Contributing & Support

Contributions are welcome. See CONTRIBUTING.md for more details.

See DEVELOPING.md for how to work with this repo locally.