Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/sanchedev/fraxel/llms.txt

Use this file to discover all available pages before exploring further.

The <camera> node controls what portion of the game world is visible by applying a viewport transform to everything it contains. Place the camera at the root of your scene tree and add all world objects as its children — the camera then translates, scales, and clips the canvas so that the camera’s position maps to the centre of the screen.
All children of a <camera> are drawn in world space relative to the camera’s position. A child at [200, 100] will appear at the centre of the screen when the camera is also at [200, 100].

Basic Usage

import { useNode } from 'fraxel/hooks'
import { PrimaryNode } from 'fraxel'

function GameScene() {
  const camera = useNode(PrimaryNode.Camera)

  return (
    <camera ref={camera} position={[0, 0]} zoom={1}>
      <sprite textureId={BACKGROUND} />
      <sprite textureId={PLAYER} position={[80, 50]} />
    </camera>
  )
}

Props

PropTypeDefaultDescription
positionVectorLike[0, 0]Camera position in world space. The point that maps to screen centre
zoomnumber1Zoom level. 2 makes everything appear twice as large; 0.5 zooms out
Both props are reactive — you can pass a SignalGetter to either for live updates without calling any methods.

Following a Target

Call camera.node.follow(target) to make the camera continuously track a node’s globalPosition. Pass undefined to stop following and return to manual position control.
import { useNode, useMount } from 'fraxel/hooks'
import { PrimaryNode } from 'fraxel'

function GameScene() {
  const camera = useNode(PrimaryNode.Camera)
  const player = useNode(PrimaryNode.Sprite)

  useMount(() => {
    camera.node.follow(player.node)
  })

  return (
    <camera ref={camera} zoom={2}>
      <sprite ref={player} textureId={PLAYER} position={[40, 50]} />
      <sprite textureId={BACKGROUND} />
    </camera>
  )
}

Methods

MethodDescription
follow(target)Makes the camera track the target node’s globalPosition every frame
follow(undefined)Stops following; the camera uses its own position property instead

How the Transform Works

Each frame, before drawing its children, the camera performs four canvas operations:
  1. Translate to centrectx.translate(width / 2, height / 2). The world origin is now at the middle of the screen.
  2. Scale by zoomctx.scale(zoom, zoom). Applies the zoom factor.
  3. Translate by -positionctx.translate(-posX, -posY) where posX/posY is either the camera’s own position or the followed target’s globalPosition.
  4. Draw children — all child nodes render into the transformed context.
After all children are drawn the canvas state is restored with ctx.restore(), so nothing outside the camera’s subtree is affected.

Scrolling World Example

The following side-scroller follows the player but switches to a fixed position when the player is too close to the left edge of the level:
import { useNode, useMount, useEvent } from 'fraxel/hooks'
import { PrimaryNode, Vector2 } from 'fraxel'

function SideScroller() {
  const camera = useNode(PrimaryNode.Camera)
  const player = useNode(PrimaryNode.Sprite)

  useMount(() => {
    camera.node.follow(player.node)
  })

  useEvent(player, 'updated', () => {
    // When the player is near the left edge, stop following and lock the camera
    const pos = player.node.globalPosition
    if (pos.x < 96) {
      camera.node.follow(undefined)
      camera.node.position = new Vector2(96, camera.node.position.y)
    } else {
      camera.node.follow(player.node)
    }
  })

  return (
    <camera ref={camera} zoom={1}>
      <sprite ref={player} textureId={PLAYER} position={[0, 80]} />
      <sprite textureId={LEVEL_BG} />
    </camera>
  )
}
The useEvent listener fires every frame (the 'updated' event) and reads the player’s world position. When the player is within 96 px of the left edge, follow(undefined) disengages target tracking and the camera’s own position is set directly. Once the player moves further right, follow(player.node) re-engages. For movement and physics that drive the player node, see Physics. For the hooks used here, see API: Hooks (Core).

Build docs developers (and LLMs) love