Quick Start

Write and run your first Chasm program in five minutes.

This page walks through writing and running a real Chasm program, explaining decisions along the way.

Hello, World

Create a file called hello.chasm:

print("Hello, Chasm!")

Run it:

chasm run hello.chasm
# Hello, Chasm!

Chasm is a scripting language — top-level statements run directly, no main() wrapper needed. print writes a value followed by a newline and works on int, float, bool, and string.

Adding Variables

name = "world"
x    = 42
msg  = "hello #{name}, x is #{x}"
print(msg)

name, x, and msg are local variables. The compiler infers their types: string, int, and string.

The #{} syntax interpolates any expression directly into a string. No str(x) calls needed.

Functions

Use def for public functions and defp for private helpers. Private functions require an explicit return type annotation:

defp fib(n :: int) :: int do
  if n <= 1 do
    return n
  end
  return fib(n - 1) + fib(n - 2)
end

print(fib(10))   # 55

Parameters always need a type annotation (n :: int). The :: int after the parameter list is the return type. return exits early; the last expression is also implicitly returned.

Structs

A struct groups related values into a single named type. Structs are value types — copying a struct copies all its fields:

defstruct Vec2 do
  x :: float
  y :: float
end

defp length(v :: Vec2) :: float do
  sqrt(v.x * v.x + v.y * v.y)
end

pos = Vec2{ x: 3.0, y: 4.0 }
print(length(pos))   # 5

pos2 = pos with { x: 0.0 }   # copy with x overridden
print(pos2.y)                  # 4, y is unchanged

The with keyword creates a modified copy of a struct. Only the listed fields change.

Arrays

An array is an ordered collection of values. The argument to array_new is the initial capacity — it starts empty (len = 0):

scores = array_new(4)   # capacity 4, length 0
scores.push(100)
scores.push(85)
scores.push(92)

print(scores.len)       # 3, elements stored
print(scores.cap)       # 4, space reserved
print(scores.get(0))    # 100

total = 0
for i in 0..scores.len do
  total = total + scores.get(i)
end
print(total)            # 277

len is how many elements are stored. cap is how much space is reserved before the array needs to grow.

A Game Loop Script

Chasm shines in game engines. Here is a minimal script that moves a player with the arrow keys:

@player_x  = 0.0
@player_y  = 0.0
@speed  = 200.0

def on_tick(dt :: float) do
  dx :: frame = 0.0
  if key_down(key_code(:right)) do dx = @speed end
  if key_down(key_code(:left))  do dx = 0.0 - @speed end
  @player_x = copy_to_script(@player_x + dx * dt)
end

def on_draw() do
  clear_background(0x181820ff)
  draw_rectangle(@player_x, @player_y, 32.0, 32.0, 0x4488ffff)
end

Run it with:

chasm run --engine raylib player.chasm

on_tick and on_draw are both required — the engine will not start without them. on_draw must call clear_background to clear the previous frame before drawing.

key_code(:right) converts the atom name to the integer keycode that key_down expects. See the Raylib API for the full list of supported key names.

The @ prefix marks a module attribute — unlike local variables, they survive across calls to on_tick. Attributes default to script lifetime: they persist until the script is hot-reloaded. See Attributes & Lifetimes for the full picture.

Next step:

Continue to Variables & Types for a complete tour of the type system.