Hot Reload

How hot reload works in Chasm, what resets, what survives, and how to design reload-friendly scripts.

Hot reload lets you edit a running script and see the changes immediately, without restarting the engine or losing game state. It is one of Chasm's most useful features for iteration.

What Happens on Reload

When you hot-reload a Chasm script:

  1. The new .chasm file is compiled to a new shared library
  2. The engine swaps in the new code at the next safe point
  3. The script arena is reset, all @script attrs reinitialize to their declared defaults
  4. The persistent arena is not reset, all @persistent attrs keep their values
  5. The frame arena resets normally at the next tick (it always does)
  6. on_init is not called again

The result: your game state resets (enemy positions, score, player position) but loaded assets remain (fonts, textures, level data).

The Three Lifetimes and Reload

LifetimeOn reload
frameResets every tick regardless, unaffected by reload
scriptResets to declared default, use for game state
persistentSurvives, use for loaded assets and save data
@score      :: script     = 0        # resets on reload, good for score
@high_score :: persistent = 0        # survives, never lost mid-session
@font       :: persistent = 0        # survives, only loaded once in on_init
@player_x   :: script     = 100.0   # resets, player restarts at 100

Designing for Reload

Put game state in script

Any state you want to reset when you fix a bug and reload goes in script:

@lives    :: script = 3
@player_x :: script = 100.0
@player_y :: script = 300.0
@enemies  :: script = array_fixed(32, Enemy{})

Reload during playtesting → you start fresh without restarting the engine.

Put assets in persistent

Load assets in on_init into persistent attrs. They survive reload:

@font    :: persistent = 0
@sprites :: persistent = 0

def on_init() do
  @font    = load_font("assets/mono.ttf")
  @sprites = load_texture("assets/sprites.png")
end

Because on_init is not called on reload, loading is a one-time cost per process.

Watch mode

The CLI watch command triggers hot-reload automatically whenever the file changes:

chasm watch --engine raylib game.chasm

Edit and save → the engine reloads within milliseconds. No key press needed.

What You Cannot Hot-Reload

  • Struct layout changes, if you add or remove fields from a defstruct, script attrs that hold that struct will reinitialize from the new defaults, which is safe. However, persistent attrs of that struct type may have stale layout and should be treated as if they were reset.
  • on_init changes, changes to on_init only take effect on the next full process restart, since on_init does not run on reload.
  • Native code, changes to engine C code require recompiling the engine binary.

Common Reload Pattern

A typical script structured for clean hot-reload:

# Loaded once, survives all reloads
@font    :: persistent = 0
@level   :: persistent = ""

# Game state, resets on reload, letting you iterate quickly
@score   :: script = 0
@player  :: script = Player{ x: 100.0, y: 300.0 }
@enemies :: script = array_fixed(32, Enemy{})
@phase   :: script = :intro

def on_init() do
  # Only runs once per process start
  @font  = load_font("assets/mono.ttf")
  @level = file_read("levels/1.json")
end

def on_tick(dt :: float) do
  update(@phase, dt)
end

def on_draw() do
  render(@phase)
end

With this structure:

  • Fix a bug and save → enemies reset, player resets, score resets, phase resets to :intro
  • The font and level data are still loaded, no flicker, no reload stall
  • You are immediately back in a clean game state to test your fix