Skip to main content
The 2D camera system allows you to create scrolling worlds, follow players, zoom in and out, and rotate the view. Talon provides full access to Raylib’s Camera2D system.

Understanding Camera2D

A 2D camera transforms the coordinate system, allowing you to:
  • Follow a moving player
  • Create parallax scrolling effects
  • Zoom in and out of the game world
  • Rotate the entire view

Creating a Camera

Create a Camera2D with four parameters:
import "raylib" for Camera2D, Vector2

var camera = Camera2D.new(
  Vector2.new(screenWidth / 2.0, screenHeight / 2.0), // offset
  Vector2.new(player.x, player.y),                    // target
  0.0,                                                 // rotation
  1.0                                                  // zoom
)

Camera Properties

  • offset: The camera’s position on the screen (usually screen center)
  • target: The point in the world the camera is looking at
  • rotation: Camera rotation in degrees
  • zoom: Camera zoom level (1.0 = normal, 2.0 = 2x zoom)
Set the offset to the screen center to make the camera target appear in the middle of the screen.

Using the Camera

Wrap your drawing code between beginMode2D() and endMode2D():
Raylib.beginDrawing()
Raylib.clearBackground(Color.RayWhite)

// Start camera mode
Raylib.beginMode2D(camera)

// Draw world elements (these are transformed by camera)
Raylib.drawRectangleRec(player, Color.Red)
for (building in buildings) {
  Raylib.drawRectangleRec(building, Color.Gray)
}

// End camera mode
Raylib.endMode2D()

// Draw UI elements (not affected by camera)
Raylib.drawText("Score: %(score)", 10, 10, 20, Color.Black)

Raylib.endDrawing()
Draw UI elements outside of beginMode2D() / endMode2D() so they stay fixed on screen.

Following the Player

Update the camera target to follow the player:
while (!Raylib.windowShouldClose()) {
  // Handle player movement
  if (Raylib.isKeyDown(KeyCode.KEY_RIGHT)) {
    player.x = player.x + 2
  } else if (Raylib.isKeyDown(KeyCode.KEY_LEFT)) {
    player.x = player.x - 2
  }

  // Make camera follow player
  camera.target = Vector2.new(player.x + 20, player.y + 20)

  // Draw
  Raylib.beginDrawing()
  Raylib.clearBackground(Color.RayWhite)

  Raylib.beginMode2D(camera)
  Raylib.drawRectangleRec(player, Color.Red)
  Raylib.endMode2D()

  Raylib.endDrawing()
}

Camera Zoom

Implement smooth zooming with the mouse wheel:
import "math" for Math

// Get mouse wheel movement
var wheelMove = Raylib.getMouseWheelMove()

// Apply exponential zoom scaling for smooth feel
camera.zoom = Math.exp(Math.log(camera.zoom) + (wheelMove * 0.1))

// Clamp zoom to reasonable limits
if (camera.zoom > 3.0) {
  camera.zoom = 3.0
} else if (camera.zoom < 0.1) {
  camera.zoom = 0.1
}
Use exponential scaling (Math.exp and Math.log) for smooth, natural-feeling zoom.

Camera Rotation

Rotate the camera view:
if (Raylib.isKeyDown(KeyCode.KEY_A)) {
  camera.rotation = camera.rotation - 1
} else if (Raylib.isKeyDown(KeyCode.KEY_S)) {
  camera.rotation = camera.rotation + 1
}

// Clamp rotation to prevent disorientation
if (camera.rotation > 40) {
  camera.rotation = 40
} else if (camera.rotation < -40) {
  camera.rotation = -40
}

Complete Camera Example

Here’s the camera example from the Talon repository:
1

Initialize camera and world

import "raylib" for Color, Raylib, Rectangle, Vector2, Camera2D, KeyCode
import "math" for Math

var MAX_BUILDINGS = 100
var screenWidth = 800
var screenHeight = 450

Raylib.initWindow(screenWidth, screenHeight, "2D Camera Example")

var player = Rectangle.new(400, 280, 40, 40)
var buildings = []
var buildColors = []

// Generate random buildings
var spacing = 0
var i = 0
while (i < MAX_BUILDINGS) {
  var height = Raylib.getRandomValue(100, 800)
  var width = Raylib.getRandomValue(50, 200)

  buildings.add(Rectangle.new(
    -6000 + spacing,
    screenHeight - 130.0 - height,
    width,
    height
  ))

  spacing = spacing + width

  buildColors.add(Color.new(
    Raylib.getRandomValue(200, 240),
    Raylib.getRandomValue(200, 240),
    Raylib.getRandomValue(200, 250),
    255
  ))

  i = i + 1
}
2

Create camera

var camera = Camera2D.new(
  Vector2.new(screenWidth / 2.0, screenHeight / 2.0),
  Vector2.new(player.x + 20.0, player.y + 20.0),
  0.0,
  1.0
)

Raylib.setTargetFPS(60)
3

Update camera in game loop

while (!Raylib.windowShouldClose()) {
  // Player movement
  if (Raylib.isKeyDown(KeyCode.KEY_RIGHT)) {
    player.x = player.x + 2
  } else if (Raylib.isKeyDown(KeyCode.KEY_LEFT)) {
    player.x = player.x - 2
  }

  // Camera follows player
  camera.target = Vector2.new(player.x + 20, player.y + 20)

  // Camera rotation
  if (Raylib.isKeyDown(KeyCode.KEY_A)) {
    camera.rotation = camera.rotation - 1
  } else if (Raylib.isKeyDown(KeyCode.KEY_S)) {
    camera.rotation = camera.rotation + 1
  }

  // Clamp rotation
  if (camera.rotation > 40) {
    camera.rotation = 40
  } else if (camera.rotation < -40) {
    camera.rotation = -40
  }

  // Zoom with mouse wheel
  camera.zoom = Math.exp(Math.log(camera.zoom) + (Raylib.getMouseWheelMove() * 0.1))

  if (camera.zoom > 3.0) {
    camera.zoom = 3.0
  } else if (camera.zoom < 0.1) {
    camera.zoom = 0.1
  }

  // Reset camera
  if (Raylib.isKeyDown(KeyCode.KEY_R)) {
    camera.zoom = 1.0
    camera.rotation = 0.0
  }
4

Draw world with camera

  Raylib.beginDrawing()
  Raylib.clearBackground(Color.RayWhite)

  Raylib.beginMode2D(camera)

  // Draw ground
  Raylib.drawRectangle(-6000, 320, 13000, 8000, Color.DarkGray)

  // Draw buildings
  var i = 0
  while (i < MAX_BUILDINGS) {
    Raylib.drawRectangleRec(buildings[i], buildColors[i])
    i = i + 1
  }

  // Draw player
  Raylib.drawRectangleRec(player, Color.Red)

  // Draw camera target lines
  Raylib.drawLine(
    camera.target.x, -screenHeight * 10,
    camera.target.x, screenHeight * 10,
    Color.Green
  )
  Raylib.drawLine(
    -screenWidth * 10, camera.target.y,
    screenWidth * 10, camera.target.y,
    Color.Green
  )

  Raylib.endMode2D()

  // Draw UI (not affected by camera)
  Raylib.drawText("SCREEN AREA", 640, 10, 20, Color.Red)
  Raylib.drawText("Free 2D camera controls:", 20, 20, 10, Color.Black)
  Raylib.drawText("- Right/Left to move", 40, 40, 10, Color.DarkGray)
  Raylib.drawText("- Mouse Wheel to Zoom", 40, 60, 10, Color.DarkGray)
  Raylib.drawText("- A/S to Rotate", 40, 80, 10, Color.DarkGray)
  Raylib.drawText("- R to reset", 40, 100, 10, Color.DarkGray)

  Raylib.endDrawing()
}

Raylib.closeWindow()

Camera in Breakout

The Breakout example uses camera for screen shake effects:
class Game {
  construct new() {
    _camera = Camera2D.new(
      Vector2.new(0.0, 0.0),
      Vector2.new(0.0, 0.0),
      0.0,
      1.0
    )
    _camera_shake_ttl = 0.0
  }

  update() {
    // Trigger shake on collision
    if (collision) {
      _camera_shake_ttl = 10.0
    }
  }

  draw() {
    // Decrease shake over time
    _camera_shake_ttl = Math.max(0.0, _camera_shake_ttl - 0.8)

    // Note: The camera can be modified for screen shake
    // _camera.offset.y = Math.sin(_camera_shake_ttl * 2.0) * 2.0 * _camera_shake_ttl / 10.0

    _camera.beginMode2D()
    // Draw game elements
    _camera.endMode2D()
  }
}
Camera methods can be called directly on the camera instance: camera.beginMode2D() instead of Raylib.beginMode2D(camera).

Advanced Camera Techniques

Smooth Camera Following

Create a smooth camera that lerps to the target:
// Lerp function
var lerp = Fn.new { |a, b, t|
  a + (b - a) * t
}

// Smooth camera update
var targetX = player.x + 20
var targetY = player.y + 20

camera.target.x = lerp.call(camera.target.x, targetX, 0.1)
camera.target.y = lerp.call(camera.target.y, targetY, 0.1)

Camera Boundaries

Prevent the camera from showing areas outside the game world:
var worldWidth = 5000
var worldHeight = 2000

// Update camera target
camera.target = Vector2.new(player.x, player.y)

// Clamp camera to world bounds
if (camera.target.x < screenWidth / 2) {
  camera.target.x = screenWidth / 2
}
if (camera.target.x > worldWidth - screenWidth / 2) {
  camera.target.x = worldWidth - screenWidth / 2
}

Next Steps

Build docs developers (and LLMs) love