Skip to main content

Overview

As your Talon game grows, organizing your code into modules becomes essential. This guide shows you how to structure your project for maintainability and scalability.

Basic Project Structure

A simple Talon project might look like this:
my-game/
├── main.wren          # Entry point
└── game.wren          # Game logic

Entry Point (main.wren)

The entry point initializes the window and runs the game loop:
main.wren
import "raylib" for Color, Raylib
import "./game" for Game

var width = 600
var height = 800

Raylib.initWindow(width, height, "My Game")
Raylib.setTargetFPS(60)

var game = Game.new()

while (!Raylib.windowShouldClose()) {
  game.update()

  Raylib.beginDrawing()
  Raylib.clearBackground(Color.new(20, 20, 20, 255))
  game.draw()
  Raylib.endDrawing()
}

Raylib.closeWindow()

Game Logic (game.wren)

The game module contains your main game logic:
game.wren
import "raylib" for Color, Raylib, KeyCode

class Game {
  construct new() {
    _player_x = 300
    _player_y = 400
  }

  update() {
    if (Raylib.isKeyDown(KeyCode.KEY_LEFT)) {
      _player_x = _player_x - 5
    }
    if (Raylib.isKeyDown(KeyCode.KEY_RIGHT)) {
      _player_x = _player_x + 5
    }
  }

  draw() {
    Raylib.drawRectangle(_player_x, _player_y, 50, 50, Color.Blue)
  }
}

Modular Project Structure

For larger games, organize code by feature or entity type:
breakout/
├── main.wren          # Entry point
├── game.wren          # Main game class
├── ball.wren          # Ball entity
├── paddle.wren        # Paddle entity
├── brick.wren         # Brick entity
├── particle.wren      # Particle effects
├── score.wren         # Score display
├── utils.wren         # Shared constants
└── res/               # Resources
    ├── ball.png
    ├── paddle.png
    └── brick.png
This is the structure used in the examples/breakout/ demo.

Import Patterns

Importing Built-in Modules

Talon provides several built-in modules:
import "raylib" for Color, Raylib, Rectangle, Vector2, Camera2D, KeyCode, Texture2D
import "math" for Math
import "builtin" for Build

Importing Local Files

Use relative paths with the ./ prefix:
import "./game" for Game
import "./ball" for Ball, BALL_W, BALL_H
import "./paddle" for Paddle, PADDLE_SPEED, PADDLE_W
import "./brick" for Brick, BRICK_W
You can import multiple items from a single module by separating them with commas.

Sharing Constants

Create a utils.wren file for shared constants:
utils.wren
var SCREEN_WIDTH = 600
var SCREEN_HEIGHT = 800
Then import it in other files:
import "./utils" for SCREEN_WIDTH, SCREEN_HEIGHT

class Paddle {
  construct new() {
    _rec = Rectangle.new(
      SCREEN_WIDTH / 2.0 - PADDLE_W / 2.0,
      SCREEN_HEIGHT - PADDLE_H - 20.0,
      PADDLE_W,
      PADDLE_H
    )
  }
}

Complete Example: Breakout Structure

Here’s how the breakout example is organized:

main.wren

examples/breakout/main.wren
import "raylib" for Color, Raylib
import "math" for Math
import "./game" for Game

var width = 600
var height = 800

Raylib.initWindow(width, height, "Breakout")
Raylib.setTargetFPS(60)

var game = Game.new()

while (!Raylib.windowShouldClose()) {
  game.update()

  Raylib.beginDrawing()
  game.draw()
  Raylib.endDrawing()
}

Raylib.closeWindow()

game.wren

examples/breakout/game.wren
import "raylib" for Color, Raylib, Rectangle, Vector2, Texture2D
import "./brick" for Brick
import "./ball" for Ball
import "./paddle" for Paddle
import "./utils" for SCREEN_HEIGHT, SCREEN_WIDTH

class Game {
  construct new() {
    _brick_texture = Texture2D.loadTexture("res/brick.png")
    _ball_texture = Texture2D.loadTexture("res/tennis.png")
    _paddle_texture = Texture2D.loadTexture("res/paddle.png")
    _bricks = Brick.new_bricks(5, 10)
    _ball = Ball.new()
    _paddle = Paddle.new()
    _score = 0.0
  }

  update() {
    var dt = Raylib.getFrameTime()
    
    // Update ball
    _ball.rec.x = _ball.rec.x + _ball.vel.x * dt
    _ball.rec.y = _ball.rec.y + _ball.vel.y * dt
    
    // Check collisions
    // ...
  }

  draw() {
    Raylib.clearBackground(Color.new(0, 0, 0, 255))
    
    for (brick in _bricks) {
      brick.draw(_brick_texture)
    }
    
    _paddle.draw(_paddle_texture)
    _ball.draw(_ball_texture)
  }
}

ball.wren

examples/breakout/ball.wren
import "raylib" for Raylib, Rectangle, Vector2, Color
import "./paddle" for PADDLE_H
import "./utils" for SCREEN_HEIGHT, SCREEN_WIDTH

var BALL_W = 16.0
var BALL_H = 16.0
var BALL_SPEED = 300.0

class Ball {
  construct new() {
    _rec = Rectangle.new(
      SCREEN_WIDTH / 2.0 - BALL_W / 2.0,
      SCREEN_HEIGHT - BALL_H - PADDLE_H - 40.0,
      BALL_W,
      BALL_H
    )
    _vel = Vector2.new(BALL_SPEED, -BALL_SPEED)
  }

  rec { _rec }
  vel { _vel }

  draw(texture) {
    Raylib.drawTexturePro(
      texture,
      Rectangle.new(0.0, 0.0, 16.0, 16.0),
      _rec,
      Vector2.new(0.0, 0.0),
      0.0,
      Color.new(255, 255, 255, 255)
    )
  }
}

Exporting from Modules

Wren automatically exports:
  • Top-level classes
  • Top-level variables
  • Top-level constants
You can then import specific items:
// ball.wren exports: Ball class, BALL_W, BALL_H, BALL_SPEED
import "./ball" for Ball, BALL_W, BALL_H

Best Practices

Keep related code together. Each entity (Player, Enemy, Bullet) should have its own file:
entities/
├── player.wren
├── enemy.wren
└── bullet.wren
For larger games, group files by feature:
my-game/
├── main.wren
├── game.wren
├── player/
│   ├── player.wren
│   ├── input.wren
│   └── abilities.wren
├── enemies/
│   ├── enemy.wren
│   ├── ai.wren
│   └── spawner.wren
└── ui/
    ├── hud.wren
    └── menu.wren
Avoid magic numbers by defining constants:
utils.wren
var SCREEN_WIDTH = 600
var SCREEN_HEIGHT = 800
var GRAVITY = 980.0
var MAX_SPEED = 500.0
Your main file should only:
  • Import the Game class
  • Initialize the window
  • Run the game loop
  • Clean up
All game logic should be in separate modules.
Keep assets in a res/ directory:
my-game/
├── main.wren
├── game.wren
└── res/
    ├── sprites/
    │   ├── player.png
    │   └── enemy.png
    ├── audio/
    │   └── music.ogg
    └── fonts/
        └── game.ttf

Module Dependencies

Be mindful of circular dependencies. If a.wren imports b.wren and b.wren imports a.wren, you’ll have issues.
Avoid circular imports by:
  • Moving shared code to a separate module
  • Using composition instead of circular references
  • Restructuring your module hierarchy

Good Pattern

utils.wren         # Shared constants

  ├── ball.wren    # Imports utils
  ├── paddle.wren  # Imports utils
  └── brick.wren   # Imports utils

game.wren         # Imports ball, paddle, brick

main.wren         # Imports game

Example Projects

Explore these example projects in the Talon repository:

Basic Example

Simple single-file game examples/basic.wren

Breakout

Multi-module game structure examples/breakout/

Build Executable

Example with build configuration examples/build-exe/

Build WASM

Web-ready game structure examples/build-wasm/

Next Steps

Wren Language

Learn more about Wren syntax

Hot Reload

Use hot reload for rapid development

Build docs developers (and LLMs) love