Raylib is a simple, open-source C game library. Chasm ships with a complete Raylib binding, compile with --engine raylib and the full Raylib API is available in your script.
Setup
Install Raylib
On macOS:
brew install raylib
On Linux:
sudo apt install libraylib-dev
Run with the Raylib engine
chasm run --engine raylib game.chasm
The CLI compiles your script and links it against Raylib automatically.
Watch mode (hot reload)
chasm watch --engine raylib game.chasm
Save the file → the engine reloads your script instantly without restarting.
Script Structure
A Raylib Chasm script uses on_init, on_tick, and on_draw:
@player_x = 400.0
@player_y = 300.0
@speed = 200.0
def on_init() do
# Called once at startup
end
def on_tick(dt :: float) do
if key_down(key_code(:right)) do @player_x = @player_x + @speed * dt end
if key_down(key_code(:left)) do @player_x = @player_x - @speed * dt end
if key_down(key_code(:down)) do @player_y = @player_y + @speed * dt end
if key_down(key_code(:up)) do @player_y = @player_y - @speed * dt end
end
def on_draw() do
clear_background(0x181820ff)
draw_rectangle(@player_x - 16.0, @player_y - 16.0, 32.0, 32.0, 0x4488ffff)
draw_text("Move with arrow keys", 10.0, 10.0, 20, 0xffffffff)
endDrawing
| Function | Description |
|---|---|
clear_background(color) | Fill the screen with a solid color |
draw_rectangle(x, y, w, h, color) | Filled rectangle |
draw_rectangle_lines(x, y, w, h, color) | Outlined rectangle |
draw_rect_rounded(x, y, w, h, r, seg, color) | Filled rectangle with rounded corners |
draw_circle(x, y, r, color) | Filled circle |
draw_circle_lines(x, y, r, color) | Circle outline |
draw_line(x1, y1, x2, y2, color) | Line segment |
draw_line_ex(x1, y1, x2, y2, thick, color) | Line segment with thickness |
draw_triangle(x1, y1, x2, y2, x3, y3, color) | Filled triangle |
draw_ellipse(cx, cy, rx, ry, color) | Filled ellipse |
draw_poly(cx, cy, sides, radius, rot, color) | Filled regular polygon |
draw_text(text, x, y, size, color) | Text with built-in font |
measure_text(text, size) | Returns rendered width of text in pixels |
draw_fps(x, y) | Draw current FPS counter |
Colors are 0xRRGGBBAA packed integers. Common values:
white = 0xffffffff
black = 0x000000ff
red = 0xff0000ff
transparent_white = 0xffffff80 # 50% alphaInput
Keyboard
| Function | Returns | Description |
|---|---|---|
key_down(key) | bool | True every frame the key is held |
key_pressed(key) | bool | True only on the first frame the key is pressed |
key_released(key) | bool | True only on the frame the key is released |
key_up(key) | bool | True every frame the key is not held |
key_last() | int | Keycode of the most recently pressed key |
key_code(name) | int | Convert a key name atom to a keycode |
Use key_code to convert atom names to integer keycodes:
# Move while held
if key_down(key_code(:right)) do @x = @x + @speed * dt end
# Fire once per press
if key_pressed(key_code(:space)) do fire_bullet() endKey name atoms supported by key_code:
| Atom | Key |
|---|---|
:right, :left, :up, :down | Arrow keys |
:space | Space bar |
:enter | Enter / Return |
:escape | Escape |
:backspace | Backspace |
:tab | Tab |
:shift, :left_shift, :right_shift | Shift |
:ctrl, :left_ctrl, :right_ctrl | Control |
:alt, :left_alt, :right_alt | Alt |
:a – :z | Letter keys |
:0 – :9 | Digit keys |
:f1 – :f12 | Function keys |
You can also pass raw integer keycodes directly (e.g. key_down(262) for right arrow).
Mouse
| Function | Returns | Description |
|---|---|---|
mouse_x() | float | Current mouse X position |
mouse_y() | float | Current mouse Y position |
mouse_dx() | float | Mouse X delta since last frame |
mouse_dy() | float | Mouse Y delta since last frame |
mouse_down(button) | bool | True while button is held |
mouse_pressed(button) | bool | True on the first frame button is pressed |
mouse_released(button) | bool | True on the frame button is released |
mouse_wheel() | float | Mouse wheel scroll delta |
Mouse button indices: 0 = left, 1 = right, 2 = middle.
Screen
| Function | Returns | Description |
|---|---|---|
screen_width() | int | Current window width in pixels |
screen_height() | int | Current window height in pixels |
fps() | int | Current frames per second |
dt() | float | Seconds elapsed since last frame |
time() | float | Seconds elapsed since startup |
set_fps(fps) | — | Set target frame rate |
set_title(text) | — | Set window title |
set_window_size(w, h) | — | Resize the window |
toggle_fullscreen() | — | Toggle fullscreen mode |
Textures and Fonts
@player_tex :: persistent = 0
@font :: persistent = 0
def on_init() do
@player_tex = load_texture("assets/player.png")
@font = load_font("assets/mono.ttf")
end
def on_draw() do
draw_texture(@player_tex, @player_x, @player_y, 0xffffffff)
draw_text_ex(@font, "Score: #{@score}", 10.0, 10.0, 24.0, 1.0, 0xffffffff)
endLoad textures and fonts in on_init into persistent attrs so they survive hot-reload.
| Function | Returns | Description |
|---|---|---|
load_texture(path) | int | Load a texture, returns handle |
unload_texture(handle) | — | Free a texture |
draw_texture(handle, x, y, tint) | — | Draw texture at position |
draw_texture_ex(handle, x, y, rot, scale, tint) | — | Draw with rotation and scale |
draw_texture_rect(handle, sx, sy, sw, sh, dx, dy, tint) | — | Draw a sub-region |
texture_w(handle) | int | Texture width in pixels |
texture_h(handle) | int | Texture height in pixels |
load_font(path) | int | Load a font, returns handle |
draw_text_ex(font, text, x, y, size, spacing, color) | — | Draw with custom font |
Audio
@shoot_sfx :: persistent = 0
def on_init() do
init_audio()
@shoot_sfx = load_sound("assets/shoot.wav")
end
def on_tick(dt :: float) do
if key_pressed(key_code(:space)) do
play_sound(@shoot_sfx)
end
end| Function | Returns | Description |
|---|---|---|
init_audio() | — | Initialize the audio device |
load_sound(path) | int | Load a sound, returns handle |
play_sound(handle) | — | Play a sound |
stop_sound(handle) | — | Stop a sound |
sound_playing(handle) | bool | True if the sound is currently playing |
sound_volume(handle, vol) | — | Set volume (0.0–1.0) |
load_music(path) | int | Load streaming music, returns handle |
play_music(handle) | — | Start playing music |
update_music(handle) | — | Call each frame to stream music |
stop_music(handle) | — | Stop music |
music_playing(handle) | bool | True if music is playing |
music_length(handle) | float | Total duration in seconds |
music_played(handle) | float | Current playback position in seconds |
Collision
| Function | Returns | Description |
|---|---|---|
collide_rects(x1, y1, w1, h1, x2, y2, w2, h2) | bool | Rectangle vs rectangle |
collide_circles(x1, y1, r1, x2, y2, r2) | bool | Circle vs circle |
point_in_rect(px, py, rx, ry, rw, rh) | bool | Point inside rectangle |
Camera 2D
def on_draw() do
camera2d_begin(@cam_x, @cam_y, @target_x, @target_y, 0.0, @zoom)
# draw world-space objects here
camera2d_end()
end| Function | Description |
|---|---|
camera2d_begin(cx, cy, tx, ty, rot, zoom) | Begin camera-transformed drawing |
camera2d_end() | End camera drawing |
world_to_screen_x(wx, wy, cx, cy, tx, ty, rot, zoom) | Convert world X to screen X |
world_to_screen_y(wx, wy, cx, cy, tx, ty, rot, zoom) | Convert world Y to screen Y |
Gamepad
| Function | Returns | Description |
|---|---|---|
gamepad_available(pad) | bool | True if gamepad is connected (pad 0 = first) |
gamepad_button_down(pad, btn) | bool | True while button is held |
gamepad_button_pressed(pad, btn) | bool | True on first frame button pressed |
gamepad_axis(pad, axis) | float | Axis value -1.0 to 1.0 |