tiramisu/debug

Debug visualization utilities for game development.

This module provides tools for visualizing game elements during development:

Usage

Debug visualizations are added to your scene just like regular nodes:

import tiramisu/debug
import vec/vec3

pub fn view(model: Model) {
  [
    // Your game objects
    scene.Mesh(...),

    // Debug visualizations
    debug.axes("axes", vec3.zero(), 5.0),
    debug.grid("grid", 20.0, 20, debug.color_white),
    debug.sphere("target", target_position, 0.5, debug.color_red),
  ]
}

Performance Stats

Monitor your game’s performance in the update loop:

pub fn update(model: Model, msg: Msg, ctx: Context) {
  let stats = debug.get_performance_stats()
  io.println("FPS: " <> float.to_string(stats.fps))
  // ...
}

Physics Debugging

Visualize physics colliders in your view function:

pub fn view(model: Model, ctx: Context) {
  // Enable collider wireframes
  case ctx.physics_world, model.debug_mode {
    option.Some(physics_world), True ->
      debug.show_collider_wireframes(physics_world, True)
    _, _ -> Nil
  }
  // ... return scene nodes
}

Types

Performance statistics collected from the game engine.

Contains real-time performance metrics useful for optimization and debugging.

Fields

  • fps: Current frames per second (smoothed average)
  • frame_time: Time in milliseconds to render the last frame
  • draw_calls: Number of draw calls in the last frame (lower is better)
  • triangles: Total number of triangles rendered in the last frame
  • memory_mb: Estimated GPU memory usage in megabytes
pub type PerformanceStats {
  PerformanceStats(
    fps: Float,
    frame_time: Float,
    draw_calls: Int,
    triangles: Int,
    memory_mb: Float,
  )
}

Constructors

  • PerformanceStats(
      fps: Float,
      frame_time: Float,
      draw_calls: Int,
      triangles: Int,
      memory_mb: Float,
    )

Values

pub fn axes(
  id: id,
  origin: vec3.Vec3(Float),
  size: Float,
) -> scene.Node(id)

Create coordinate axes visualization at a given position.

Displays X (red), Y (green), and Z (blue) axes to help visualize object orientation and world coordinates. This is particularly useful for understanding transformations and rotations.

Parameters

  • id: Unique identifier for this debug node
  • origin: Position where the axes should be centered
  • size: Length of each axis line

Example

import tiramisu/debug
import vec/vec3

// Show world axes at origin
debug.axes("world-axes", vec3.zero(), 5.0)

// Show object-local axes
debug.axes("player-axes", player_position, 2.0)
pub fn bounding_box(
  id: id,
  min: vec3.Vec3(Float),
  max: vec3.Vec3(Float),
  color: Int,
) -> scene.Node(id)

Create a debug wireframe box defined by minimum and maximum corners.

Useful for visualizing bounding boxes, collision volumes, or spatial regions.

Parameters

  • id: Unique identifier for this debug node
  • min: Minimum corner position (x, y, z)
  • max: Maximum corner position (x, y, z)
  • color: Hex color code (e.g., 0xff0000 for red)

Example

import tiramisu/debug
import vec/vec3

// Visualize a bounding box from (-1, -1, -1) to (1, 1, 1)
debug.bounding_box(
  "bounds",
  vec3.Vec3(-1.0, -1.0, -1.0),
  vec3.Vec3(1.0, 1.0, 1.0),
  debug.color_cyan,
)
pub fn box_from_transform(
  id: id,
  t: transform.Transform,
  color: Int,
) -> scene.Node(id)

Create a debug bounding box from a transform.

Convenient helper that extracts position and scale from a transform to create a bounding box visualization. Useful for visualizing object bounds in world space.

Parameters

  • id: Unique identifier for this debug node
  • t: Transform containing position and scale information
  • color: Hex color code for the box wireframe

Example

import tiramisu/debug
import tiramisu/transform
import vec/vec3

let cube_transform = transform.new()
  |> transform.with_position(vec3.Vec3(0.0, 2.0, 0.0))
  |> transform.with_scale(vec3.Vec3(2.0, 2.0, 2.0))

// Visualize the cube's bounds
debug.box_from_transform("cube-bounds", cube_transform, debug.color_orange)
pub fn collider(
  id: id,
  shape: physics.ColliderShape,
  transform: transform.Transform,
  color: Int,
) -> scene.Node(id)

Visualize a physics collider shape at a given transform.

This function converts a physics collider into debug visualization nodes that can be added to your scene for debugging physics shapes.

Example

import tiramisu/debug
import tiramisu/physics
import tiramisu/transform
import vec/vec3

pub fn view(model: Model) {
  let body_transform = transform.at(position: vec3.Vec3(0.0, 5.0, 0.0))
  let collider = physics.Box(width: 2.0, height: 2.0, depth: 2.0)

  [
    // Your normal scene nodes...
    // Debug visualization for the collider
    debug.collider(
      id: "player-collider-debug",
      shape: collider,
      transform: body_transform,
      color: debug.color_green,
    ),
  ]
}
pub const color_black: Int

Black color (0x000000) - Rarely used for debug visualization (not visible on dark backgrounds).

pub const color_blue: Int

Blue color (0x0000ff) - Commonly used for information, water, or Z-axis visualization.

pub const color_cyan: Int

Cyan color (0x00ffff) - Commonly used for secondary information or water/ice elements.

pub const color_green: Int

Green color (0x00ff00) - Commonly used for success, active states, or Y-axis visualization.

pub const color_magenta: Int

Magenta color (0xff00ff) - Commonly used for special markers or UI highlights.

pub const color_orange: Int

Orange color (0xffa500) - Commonly used for alerts, spawn points, or intermediate states.

pub const color_purple: Int

Purple color (0x800080) - Commonly used for special objects, power-ups, or tertiary markers.

pub const color_red: Int

Red color (0xff0000) - Commonly used for errors, warnings, or X-axis visualization.

pub const color_white: Int

White color (0xffffff) - Commonly used for grids, general outlines, or default markers.

pub const color_yellow: Int

Yellow color (0xffff00) - Commonly used for caution, highlights, or important markers.

pub fn cross(
  id: id,
  position: vec3.Vec3(Float),
  size: Float,
  color: Int,
) -> List(scene.Node(id))

Create a 3D cross (three perpendicular lines) at a position.

Useful for marking points in 3D space with a distinctive visual marker that’s visible from all angles, unlike a simple point.

Parameters

  • id: Unique identifier for all three lines (they share the same ID)
  • position: Center position of the cross
  • size: Total length of each line (extends size/2 in each direction)
  • color: Hex color code for all three lines

Returns

A list of three line nodes (X, Y, and Z aligned).

Example

import tiramisu/debug
import vec/vec3

// Mark a target position with a visible cross
debug.cross(
  "target-marker",
  target_position,
  1.0,
  debug.color_red,
)
pub fn get_performance_stats() -> PerformanceStats

Get current performance statistics from the renderer.

Returns real-time performance metrics that can be used for optimization, profiling, or displaying debug information to the user.

Example

import tiramisu/debug
import gleam/io
import gleam/float

pub fn update(model: Model, msg: Msg, ctx: Context) {
  let stats = debug.get_performance_stats()

  // Log performance issues
  case stats.fps <. 30.0 {
    True -> io.println("Warning: Low FPS!")
    False -> Nil
  }

  // Track draw calls for optimization
  case stats.draw_calls > 100 {
    True -> io.println("Too many draw calls: " <> int.to_string(stats.draw_calls))
    False -> Nil
  }

  // ... rest of update logic
}
pub fn grid(
  id: id,
  size: Float,
  divisions: Int,
  color: Int,
) -> scene.Node(id)

Create a grid on the ground plane (XZ plane).

Useful for visualizing the ground, spatial reference, or movement areas.

Parameters

  • id: Unique identifier for this debug node
  • size: Total size of the grid (e.g., 20.0 creates a 20×20 grid)
  • divisions: Number of grid divisions (higher = more lines)
  • color: Hex color code for all grid lines

Example

import tiramisu/debug

// Create a 20×20 grid with 20 divisions
debug.grid("ground-grid", 20.0, 20, debug.color_white)
pub fn line(
  id: id,
  from: vec3.Vec3(Float),
  to: vec3.Vec3(Float),
  color: Int,
) -> scene.Node(id)

Create a debug line between two points.

Useful for visualizing connections, trajectories, or directions.

Parameters

  • id: Unique identifier for this debug node
  • from: Starting point of the line
  • to: Ending point of the line
  • color: Hex color code (e.g., 0xff00ff for magenta)

Example

import tiramisu/debug
import vec/vec3

// Draw a line from origin to target
debug.line(
  "path-line",
  vec3.Vec3(0.0, 0.0, 0.0),
  target_position,
  debug.color_green,
)
pub fn path(
  id: fn(Int) -> id,
  points: List(vec3.Vec3(Float)),
  color: Int,
) -> List(scene.Node(id))

Create multiple lines forming a path through points.

Useful for visualizing paths, trajectories, AI navigation routes, or animation curves. Connects consecutive points with lines.

Parameters

  • id: Function that generates unique IDs for each line segment (receives index)
  • points: List of positions to connect
  • color: Hex color code for all path lines

Returns

A list of line nodes, one for each segment between consecutive points.

Example

import tiramisu/debug
import vec/vec3

let waypoints = [
  vec3.Vec3(0.0, 0.0, 0.0),
  vec3.Vec3(5.0, 2.0, 0.0),
  vec3.Vec3(10.0, 0.0, 5.0),
  vec3.Vec3(15.0, 3.0, 10.0),
]

// Visualize a patrol path
debug.path(
  fn(i) { "patrol-segment-" <> int.to_string(i) },
  waypoints,
  debug.color_yellow,
)
pub fn point(
  id: id,
  position: vec3.Vec3(Float),
  size: Float,
  color: Int,
) -> scene.Node(id)

Create a debug point marker at a position.

Useful for marking specific coordinates, waypoints, or spawn points.

Parameters

  • id: Unique identifier for this debug node
  • position: World position of the point
  • size: Size of the point marker
  • color: Hex color code (e.g., 0xffa500 for orange)

Example

import tiramisu/debug

// Mark spawn points
debug.point("spawn-1", spawn_pos_1, 0.3, debug.color_green)
debug.point("spawn-2", spawn_pos_2, 0.3, debug.color_green)
pub fn ray(
  id: id,
  origin: vec3.Vec3(Float),
  direction: vec3.Vec3(Float),
  length: Float,
  color: Int,
) -> scene.Node(id)

Create a debug ray from an origin point in a direction.

Useful for visualizing raycasts, shooting directions, or look-at vectors.

Parameters

  • id: Unique identifier for this debug node
  • origin: Starting point of the ray
  • direction: Direction vector (should be normalized for predictable length)
  • length: Length of the ray
  • color: Hex color code (e.g., 0xff0000 for red)

Example

import tiramisu/debug
import vec/vec3

// Visualize a forward-facing ray
debug.ray(
  "look-ray",
  camera_position,
  camera_forward,
  10.0,
  debug.color_blue,
)
pub fn show_collider_wireframes(
  physics_world: physics.PhysicsWorld(id),
  enabled: Bool,
) -> Nil

Enable or disable debug wireframe visualization for all physics colliders in the scene.

This function uses Rapier’s built-in collider visualization which renders wireframes for all physics bodies in the physics world. The wireframes update automatically each frame as objects move and rotate.

Note: This function requires access to the physics world, which is available in the view function via the Context parameter.

Example

import tiramisu/debug
import gleam/option

pub fn view(model: Model, ctx: tiramisu.Context) {
  // Enable/disable debug visualization based on model state
  case ctx.physics_world, model.debug_mode {
    option.Some(physics_world), True -> {
      debug.show_collider_wireframes(physics_world, True)
    }
    _, _ -> Nil
  }

  // Return scene as normal
  [
    scene.Mesh(
      id: "cube",
      geometry: geometry,
      material: material,
      transform: transform,
      physics: option.Some(physics_body),  // Will show wireframe when enabled
    ),
    // ... more scene nodes
  ]
}
pub fn sphere(
  id: id,
  center: vec3.Vec3(Float),
  radius: Float,
  color: Int,
) -> scene.Node(id)

Create a debug wireframe sphere.

Useful for visualizing positions, ranges, trigger zones, or spherical collision shapes.

Parameters

  • id: Unique identifier for this debug node
  • center: Center position of the sphere
  • radius: Radius of the sphere
  • color: Hex color code (e.g., 0x00ff00 for green)

Example

import tiramisu/debug
import vec/vec3

// Visualize a pickup range around a player
debug.sphere(
  "pickup-range",
  player_position,
  2.5,
  debug.color_yellow,
)
pub fn with_collider_wireframes(
  nodes: List(scene.Node(id)),
  color: Int,
) -> List(scene.Node(id))

@deprecated Use show_collider_wireframes with the physics world from context instead.

This function is kept for backwards compatibility but will be removed in a future version.

Search Document