Documentation/Libraries

raylib graphics and games

On this page

A native 2D/procedural-3D graphics and WAV audio binding to pinned raylib 6.0 (SDL2 / OpenGL 3.3).

python3 libraries/raylib/build.py
build/selfhost/stage3 run --manifest examples/circle-demo/basic.toml
build/selfhost/stage3 run --manifest examples/space-invaders/basic.toml

The first command needs cc, ar, SDL2/OpenGL development libraries, the vendored source release and its already-built SDL2 core archive. It writes only inside this library directory; it does not invoke upstream make. It writes build/raylib.blib in this directory. For the initial pinned-core build on a clean checkout, run python3 tools/build_raylib.py build/selfhost/stage3; subsequent binding builds can use the library-local command above.

Consumers can copy that file alone and point their dependency at it. It contains the BASIL declarations/templates/native code, both static archives and raylib’s license and bundled upstream third-party notices. The package does not require this source tree. System SDL2/GL, libc and their display-driver dependencies remain declared system dependencies; this first graphics backend uses the documented /usr/bin/cc hosted link bridge. Ordinary BASIL programs still use the integrated standalone linker.

IMPORT Raylib
Raylib.InitWindow(640,400,"Hello from BASIC")
Raylib.SetTargetFPS(60)
WHILE NOT Raylib.WindowShouldClose()
    Raylib.BeginDrawing()
    Raylib.ClearBackground(Raylib.Rgba(12,18,32))
    Raylib.DrawCircle(320,200,70,Raylib.Rgba(64,220,190))
    Raylib.DrawText("BASIC + raylib",30,30,28,Raylib.Rgba(240,245,255))
    Raylib.EndDrawing()
WEND
Raylib.CloseWindow()

The original 2D subset exposes InitWindow/CloseWindow, SetConfigFlags, WindowShouldClose, SetTargetFPS, BeginDrawing/EndDrawing, ClearBackground, DrawRectangle, DrawCircle, DrawText, IsKeyDown/IsKeyPressed, GetFrameTime/GetTime, TakeScreenshot and Rgba. Key codes are raylib’s integer constants (arrows 263/262, space32, enter257, escape256, letters ASCII uppercase). Hidden-window flag128 must precede InitWindow. Colors are checked RGBA components packed into one INTEGER. Positions/sizes are checked INTEGER values. Time results are DOUBLE seconds.

Calls declare NumericError, StringError and RaylibError. RaylibError codes: 1 already initialized/flags too late, 2 shutdown registration failed, 3 window not ready after creation, 4 window required, 5 invalid begin/end/draw state, 6 screenshot export failed. Upstream may still terminate on unrecoverable graphics initialization/driver failures; it is a trusted native backend, not a sandbox. Screenshot paths are preserved; failed export is a typed error, not just a log.

There is one process-global window, restricted to the coordinator by checked foreign effects. Native calls do not suspend or retain borrowed BASIL pointers. The C adapter copies NUL-terminated text (rejecting embedded NUL) and retains its own title until CloseWindow. Temporary drawing text is freed after the call. Repeated CloseWindow is harmless. Normal process exit has an atexit fallback; this API is not yet a lexical RESOURCE handle and cannot promise cleanup on fatal OOM/driver termination. Do not call raw adapter symbols from application code. The shim memcpy-loads unaligned string headers and adapts BASIL stack/RAX/R10 to SysV. Native recipe , format , acceptance .

File-loaded models, textures, streaming/compressed audio, arbitrary shaders/materials and the rest of raylib remain future binding expansion; the upstream library’s capabilities are not all exposed here. The procedural 3D API below is implemented.

Procedural 3D, meshes and mouse input

Verified 2026-11-11 with focused source/prebuilt, ownership, render-state, frustum and guarded-coroutine acceptance under tests/. The compiler, standard library, native runtime and upstream raylib sources are unchanged by this extension.

IMPORT Raylib
Raylib.InitWindow(800,600,"3D BASIC")
Raylib.SetTargetFPS(60)
mesh=Raylib.NewMesh()
FOR side=0 TO 5
    Raylib.AddCubeFace(mesh,0,0,0,side,Raylib.Rgba(80,180,130))
NEXT
Raylib.UploadMesh(mesh)
camera=Raylib.Camera3D {Position=Raylib.Vec3(4.0,3.0,5.0),Target=Raylib.Vec3(0.0,0.0,0.0),Up=Raylib.Vec3(0.0,1.0,0.0)}
WHILE NOT Raylib.WindowShouldClose()
    IF Raylib.IsKeyPressed(256) THEN EXIT WHILE
    Raylib.BeginDrawing()
    Raylib.ClearBackground(Raylib.Rgba(140,185,200))
    Raylib.BeginMode3D(camera)
    Raylib.DrawMesh(mesh,Raylib.Vec3(0.0,0.0,0.0))
    Raylib.EndMode3D()
    Raylib.EndDrawing()
WEND
Raylib.CloseWindow()
  • Vector2/Vector3 are ordinary DOUBLE-valued records. Vec3(x,y,z) constructs Vector3. Camera3D has Position, Target, Up, Fovy (default 65) and Projection (0 perspective, 1 orthographic). Position/Target must differ and Up must not be collinear with the viewing direction. Fovy must be finite and between 0 and 179; in orthographic mode it denotes view height under the same current limit.
  • NewMesh() returns an opaque, noncopyable resource. AddQuad(mesh,a,b,c,d,color) copies counterclockwise vertices and calculates a flat normal/UVs. No BASIL pointers are retained. This builder supports up to 2,000,000 vertices.
  • AddCubeFace(mesh,x,y,z,side,color) appends just one exposed unit-cube face: 0 +X, 1 -X, 2 +Y, 3 -Y, 4 +Z, 5 -Z. The caller decides which faces are visible.
  • UploadMesh(mesh) freezes/uploads the geometry. An empty mesh is valid. DrawMesh(mesh,position,scale=1.0,tint=4294967295) requires an active 3D block, draws the retained mesh, and returns FALSE if empty/outside the frustum. Perspective culling uses the mesh bounding sphere against side/near planes; GPU clipping, depth and backface culling handle individual triangles. Scale must be finite, positive and at most 1,000,000.
  • MeshVertexCount(mesh) returns vertex count; UnloadMesh(mesh) releases its buffers early and is idempotent. Scope cleanup frees both buffers and the owner. CloseWindow releases outstanding GPU buffers before destroying the context; wrappers can subsequently clean up safely but are no longer drawable.
  • BeginMode3D(camera)/EndMode3D() nest inside BeginDrawing/EndDrawing. Mismatched/nested 3D begins and EndDrawing during 3D are checked errors.
  • SetFog(color,start,finish) enables distance fog for these procedural meshes. It uses a bundled OpenGL 3.3 shader, with no runtime shader files. Bounds must be finite and 0 <= start < finish. Vertex colors supply baked face lighting.
  • DrawnMeshCount() and DrawnFaceCount() report submitted meshes/quads since the latest BeginMode3D, not GPU occlusion-query results.
  • GetMouseDelta() returns Vector2, CaptureMouse(TRUE/FALSE) captures/releases the pointer, and GetScreenWidth/GetScreenHeight follow window resizing.

RaylibError 7 means a closed/frozen/otherwise invalid mesh operation; 8 means fog shader creation failed. NumericError covers invalid geometry/camera values. All calls stay on the coordinator. Both 2D and 3D assembly adapters enter C via __basic_rt_v1_9288, using the existing guarded foreign stack rather than a small coroutine stack for OpenGL/driver calls.

The original bridge.c and bridge.S entry files include the new 3D units, so existing two-file native build recipes remain compatible. The bridge compiles a narrowly configured copy of the pinned rmodels.c into its own translation unit; file loaders and mesh generators are disabled. No upstream source is modified.

python3 libraries/raylib/build.py
python3 libraries/raylib/tests/run.py

Tests render real hidden windows and therefore require a working display/OpenGL implementation. Set BASIC_COMPILER to select an existing compiler. All generated fixtures, copied packages and the verification report stay in tests/build/.

Translucent meshes and scene lighting

DrawMeshTransparent(mesh,position,scale=1.0,tint=4294967295) uses vertex/tint alpha, keeps depth testing, disables depth writes and backface culling for this draw, then restores both states. Draw opaque geometry first, then transparent meshes in back-to-front order. This is conventional alpha blending; it does not sort triangles or implement order-independent transparency.

SetSceneLighting(red,green,blue) sets floating-point RGB gains (each finite, 0 through 4) for procedural mesh colors. It requires an open window and applies to subsequent mesh draws until changed or the window closes. Default is (1.0,1.0,1.0). It does not affect 2D drawing. Fog preserves surface alpha; fog color is supplied separately and is not multiplied by lighting gains.

The source and relocated-package tests also use Pillow to check actual rendered pixels for alpha blending, depth-write restoration, RGB gains, and fog alpha. These are binding changes using the existing compiler ABI.

Vector lines, input and audio (2026-11-12)

The package now includes:

  • DrawLine(x1,y1,x2,y2,color,width=1) with INTEGER coordinates and width 1..32.
  • GetMousePosition() AS Vector2, IsMouseButtonDown(button), IsMouseButtonPressed(button), and ToggleFullscreen(). Mouse buttons are 0..6 (left 0, right 1). These follow the existing coordinator/window/drawing guards.
  • InitAudioDevice() AS BOOLEAN, IsAudioDeviceReady() AS BOOLEAN, and CloseAudioDevice(). Initialization returns FALSE if no device is available, allowing a game to continue silently. Audio has a lifecycle separate from the window.
  • Opaque owning Sound, LoadSound(path), PlaySound(sound), StopSound(sound), IsSoundPlaying(sound), SetSoundVolume(sound,volume) (0..1), and SetSoundPitch(sound,pitch) (greater than 0, at most 4).
IMPORT Raylib
IF Raylib.InitAudioDevice() THEN
    sound=Raylib.LoadSound("effect.wav")
    Raylib.SetSoundVolume(sound,0.5)
    Raylib.PlaySound(sound)
    ' Keep the owner in scope while playing; dropping it unloads the sound.
END IF
Raylib.CloseAudioDevice()

Sound handles cannot be copied. Ordinary calls borrow them; use TAKE to transfer ownership. Automatic cleanup stops/unloads the clip. Closing the audio device unloads active clips and safely invalidates existing owners, whose later cleanup remains safe. Repeated CloseAudioDevice is harmless. A successful device may be reinitialized, but old closed handles remain invalid. An atexit fallback closes the device. No borrowed BASIL memory is retained by the audio thread.

New RaylibError codes: 10 audio device/handle unavailable or closed, 11 sound load failed, 12 unsupported internal operation. Invalid volume, pitch, line width and mouse indices produce NumericError. Only WAV decoding is compiled in for this initial audio API. Synthesized PCM WAV files can be generated entirely in BASIL; ~/src/bt/star-wars/src/audio.bas is one example. Music streams, compressed codecs, procedural audio callbacks and recording are not exposed yet.

The existing pinned SDL2 core was built with audio excluded. build.py now builds only the pinned upstream raudio.c as a separate object with WAV support and packages it in the adapter archive. It does not modify or rebuild the pinned core. Non-audio clients do not call InitAudioDevice, and CloseWindow does not close an independently managed audio device.

Acceptance with an available display/audio device:

BASIC_RAYLIB_TEST_AUDIO=1 python3 libraries/raylib/tests/run.py

This exercises loading, playback/status, stop, range checks, missing files, closed handles and ownership through both source and moved-package consumers, as well as existing 2D/3D/transparency/fog/lighting regression checks. Without the environment flag, the original graphics checks run without requiring audio hardware.

From the BASIL documentation. View source ↗

Find your way around BASIL

Type to search the manual.