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.

2D nodes are the visual and spatial building blocks of a Fraxel scene. Every 2D node extends Node2D, which adds a position property (Vector2) and both local and global position helpers (position for local space, globalPosition for world space). They compose hierarchically — translating a parent node moves all of its children with it. For a conceptual overview see the Nodes guide.

Base: Node2D

All 2D nodes inherit these shared props and properties from Node2D (which itself extends Node).

Shared props (all 2D nodes)

position
VectorLike | Reactive<VectorLike>
Local position of the node in its parent’s coordinate space. Accepts a [x, y] tuple, { x, y } object, or a single number (square). Defaults to Vector2.ZERO.
id
string | symbol
Optional identifier for this node. Used with child() to find descendants by path. Must match [a-zA-Z][a-zA-Z0-9-_]* when a string.
zIndex
number
Drawing order relative to siblings. Higher values render on top. Defaults to 0.
deltaIncrease
number
Multiplier applied to delta for this node and all its children. Use 0.5 for half-speed, 2 for double-speed. Defaults to 1.
script
FraxelScript<T>
Optional script to attach to this node for external logic encapsulation.

Shared instance properties

PropertyTypeDescription
positionVector2Local position. Mutable.
globalPositionVector2World-space position. Readable and settable — the setter computes the required local offset.
parentNode | undefinedThe parent node, if any.
childrenNode[]Direct child nodes.
isStartedbooleantrue once the node has been started.
isDestroyedbooleantrue once destroy() has been called.

Shared events (all nodes)

EventCallbackDescription
started() => voidFires once when the node finishes its start lifecycle.
updated(delta: number) => voidFires every frame during the update cycle. delta is seconds since last frame.
drawed(delta: number) => voidFires every frame during the draw cycle.
destroyed() => voidFires when the node is destroyed.

Shared methods (all nodes)

MethodSignatureDescription
addChild(...nodes: Node[]) => voidAdds child nodes; starts them immediately if the parent is already started.
removeChild(node: Node) => voidRemoves a direct child.
child({ path: (string | symbol)[], type: T }) => NodeInstances[T]Finds a descendant by id path and asserts type. Throws if not found.
destroy() => voidDestroys the node and all its children, emitting destroyed.
cleanEvents() => voidRemoves all event listeners. Called automatically on destroy.

Nodes

Class: Transform · Enum: PrimaryNode.Transform · JSX tag: <transform>Transform is a positioning container. It has no visual representation of its own — its sole purpose is to apply a coordinate offset to everything nested inside it. All children are drawn in the transform’s local space, so moving the transform moves all its children together.
import { useNode, useEvent } from 'fraxel/hooks'
import { PrimaryNode, Vector2 } from 'fraxel'

function Player() {
  const body = useNode(PrimaryNode.Transform)

  useEvent(body, 'updated', (delta) => {
    body.node.position.x += delta * 120
  })

  return (
    <transform ref={body} position={[100, 200]}>
      <sprite textureId={PLAYER_TEXTURE} />
      <collider shape={shapes.rectangle(32, 48)} group={['player']} collidesWith={['wall']} />
    </transform>
  )
}

Props

position
VectorLike | Reactive<VectorLike>
Local position of the transform. Moves all children. Defaults to Vector2.ZERO.
id
string | symbol
Node identifier for child() lookups.
zIndex
number
Z-order for rendering. Higher values render on top.
deltaIncrease
number
Speed multiplier applied to this node and all children.
children
Node[]
Child nodes nested inside this transform.

Notes

  • Transform is the idiomatic way to group multiple child nodes that share a world position (e.g. a sprite + collider + rigid body for a single game object).
  • For a purely logical grouping without spatial positioning, use <group> instead.
Class: Sprite · Enum: PrimaryNode.Sprite · JSX tag: <sprite>Sprite renders a texture loaded with loadTexture(). It supports sprite sheet atlases (via margin and sourceSize), independent display scaling, horizontal/vertical flipping, and a full set of canvas-based visual filters.
import { loadTexture } from 'fraxel'
import { useNode } from 'fraxel/hooks'
import { PrimaryNode, Vector2 } from 'fraxel'

const PLAYER_TEXTURE = await loadTexture('/assets/player.png')

function Player() {
  const sprite = useNode(PrimaryNode.Sprite)

  return (
    <sprite
      ref={sprite}
      textureId={PLAYER_TEXTURE}
      sourceSize={new Vector2(32, 32)}
      displaySize={new Vector2(64, 64)}
      brightness={1.2}
      modulate={[1, 0.5, 0, 1]}
    />
  )
}

Props

textureId
symbol | Reactive<symbol>
The symbol returned by loadTexture(). The texture must be loaded before the node starts.
position
VectorLike | Reactive<VectorLike>
Local position of the sprite.
margin
VectorLike | Reactive<VectorLike>
Pixel offset within the source texture. Used to select a frame from a sprite sheet.
sourceSize
VectorLike | Reactive<VectorLike>
Region of the texture to sample. Defaults to the full texture size.
displaySize
VectorLike | Reactive<VectorLike>
Rendered size on screen. Defaults to sourceSize.
flipX
boolean | Reactive<boolean>
Mirror the sprite horizontally. Defaults to false.
flipY
boolean | Reactive<boolean>
Mirror the sprite vertically. Defaults to false.
Visual filter props — all reactive, all optional:
brightness
number | Reactive<number>
Brightness multiplier. 0 = black, 1 = unchanged, 2 = fully white. Default 1.
grayscale
number | Reactive<number>
Grayscale amount. 0 = full color, 1 = fully desaturated. Default 0.
modulate
Color | Reactive<Color>
RGBA tint [r, g, b, a] where each channel is 01. Applied via multiply composite operation. Default [1, 1, 1, 1] (no tint).
contrast
number | Reactive<number>
Contrast multiplier. 0 = flat grey, 1 = unchanged, 2 = double contrast. Default 1.
saturate
number | Reactive<number>
Saturation multiplier. 0 = desaturated, 1 = unchanged, 2 = double saturation. Default 1.
hueRotate
number | Reactive<number>
Hue rotation in degrees. 0 = unchanged, 360 = full rotation. Default 0.
invert
number | Reactive<number>
Color inversion amount. 0 = normal, 1 = fully inverted. Default 0.
opacity
number | Reactive<number>
Alpha transparency. 0 = invisible, 1 = fully opaque. Default 1.

Events

EventCallbackDescription
started() => voidFires when the node is started and the texture is loaded.
updated(delta: number) => voidFires every frame. Use this to update position or filter values.
destroyed() => voidFires when the sprite is destroyed.

Methods

MethodSignatureDescription
getTexture() => Texture | undefinedReturns the currently loaded Texture object, or undefined if none is set.

Instance properties

PropertyTypeDescription
textureIdsymbol | undefinedGet/set the texture symbol. Setting updates the displayed texture immediately.
brightnessnumberGet/set the brightness filter.
grayscalenumberGet/set the grayscale filter.
modulateColorGet/set the RGBA tint.
contrastnumberGet/set the contrast filter.
saturatenumberGet/set the saturation filter.
hueRotatenumberGet/set the hue rotation in degrees.
invertnumberGet/set the inversion filter.
opacitynumberGet/set the opacity filter.
Class: Rectangle · Enum: PrimaryNode.Rectangle · JSX tag: <rectangle>Rectangle renders a filled and optionally stroked rectangle using the canvas 2D API. All color props accept RGBA tuples with channels in the 01 range. Supports reactive props for dynamic UI elements like health bars, progress indicators, or debug overlays.
import { useNode } from 'fraxel/hooks'
import { PrimaryNode } from 'fraxel'

function HealthBar({ health }: { health: () => number }) {
  const bar = useNode(PrimaryNode.Rectangle)

  return (
    <rectangle
      ref={bar}
      position={[10, 10]}
      size={() => [health() * 100, 12]}
      fillColor={[0.2, 0.8, 0.2, 1]}
      strokeColor={[0, 0, 0, 1]}
      strokeWidth={2}
    />
  )
}

Props

size
VectorLike | Reactive<VectorLike>
required
Dimensions of the rectangle [width, height]. Required — there is no default size.
position
VectorLike | Reactive<VectorLike>
Local position of the rectangle’s top-left corner. Defaults to Vector2.ZERO.
fillColor
Color | Reactive<Color>
RGBA fill color [r, g, b, a] with channels in 01. Defaults to [1, 1, 1, 1] (opaque white).
strokeColor
Color | Reactive<Color>
RGBA border color. If omitted, no border is drawn.
strokeWidth
number | Reactive<number>
Border width in pixels. Only used if strokeColor is set. Defaults to 1.

Events

Inherits all shared Node2D events: started, updated, drawed, destroyed.

Instance properties

PropertyTypeDescription
sizeVector2Current dimensions.
fillColorColorCurrent fill RGBA.
strokeColorColor | undefinedCurrent stroke RGBA, or undefined.
strokeWidthnumberCurrent border width.
Class: Text · Enum: PrimaryNode.Text · JSX tag: <text>Text renders a string on the canvas using ctx.fillText(). The text prop is reactive — pass a SignalGetter<string> to automatically re-render whenever the underlying signal changes. Style is controlled via a partial TextStyle object; unset fields fall back to TextStyle.DEFAULT.
import { useNode, useSignal } from 'fraxel/hooks'
import { PrimaryNode, FontWeight, TextAlign } from 'fraxel'

function ScoreLabel() {
  const label = useNode(PrimaryNode.Text)
  const [score] = useSignal(0)

  return (
    <text
      ref={label}
      position={[16, 16]}
      text={() => `Score: ${score()}`}
      style={{
        fontSize: 20,
        fontFamily: 'monospace',
        foregroundColor: '#ffffff',
        fontWeight: FontWeight.Bold,
        textAlign: TextAlign.Start,
      }}
    />
  )
}

Props

text
string | Reactive<string>
required
The string to render. Pass a SignalGetter<string> (i.e. () => string) for reactive updates.
position
VectorLike | Reactive<VectorLike>
Local position of the text’s top-left baseline. Defaults to Vector2.ZERO.
style
Partial<TextStyle>
Partial style overrides. Unset fields use TextStyle.DEFAULT values. See the table below.

TextStyle fields

FieldTypeDefaultDescription
foregroundColorstring'#000000'CSS color string for the text fill.
fontSizenumber16Font size in pixels.
fontFamilystring'sans-serif'CSS font family name.
fontWeightFontWeightFontWeight.NormalFontWeight.Normal or FontWeight.Bold.
textAlignTextAlignTextAlign.StartTextAlign.Start, TextAlign.Center, or TextAlign.End.

Events

Inherits all shared Node2D events: started, updated, drawed, destroyed.

Instance properties

PropertyTypeDescription
textstringThe current rendered string.
Class: Collider · Enum: PrimaryNode.Collider · JSX tag: <collider>Collider registers a collision shape with the engine’s CollisionSystem. It fires events when another collider begins, continues, or stops overlapping with it. Shapes are created via the shapes helper (shapes.rectangle(w, h) or shapes.circle(r)). Groups and collidesWith arrays control which objects interact.For full collision documentation see the Collision guide.
import { useNode, useEvent } from 'fraxel/hooks'
import { PrimaryNode, shapes } from 'fraxel'

function Enemy() {
  const collider = useNode(PrimaryNode.Collider)

  useEvent(collider, 'colliderEntered', (other) => {
    console.log('Hit by:', other.group)
  })

  return (
    <transform position={[200, 100]}>
      <sprite textureId={ENEMY_TEXTURE} />
      <collider
        ref={collider}
        shape={shapes.rectangle(32, 32)}
        group={['enemy']}
        collidesWith={['player', 'projectile']}
      />
    </transform>
  )
}

Props

shape
Shape
required
The collision shape. Use shapes.rectangle(width, height) or shapes.circle(radius) from the fraxel package.
group
string[]
required
Labels that identify this collider. Other colliders whose collidesWith includes one of these strings will detect this collider.
collidesWith
string[]
required
Groups that this collider can collide with. Collisions only fire if both colliders mutually match each other’s groups.
position
VectorLike | Reactive<VectorLike>
Local position offset of the collision shape relative to the node’s parent.

Events

EventuseEvent nameCallbackDescription
colliderEntered'colliderEntered'(collider: Collider) => voidFires once when overlap begins with another collider.
collided'collided'(collider: Collider) => voidFires every frame while two colliders overlap.
colliderExited'colliderExited'(collider: Collider) => voidFires once when overlap ends.

Instance properties

PropertyTypeDescription
shapeShapeThe collision shape (read-only).
groupSet<string>Groups this collider belongs to (read-only).
collidesWithSet<string>Groups this collider reacts to (read-only).
sizeVector2Bounding dimensions of the shape. Diameter × diameter for circles.
Enable gameConfig.testOptions.showColliders = true to visualize collision shapes during development.
Class: RayCast · Enum: PrimaryNode.RayCast · JSX tag: <ray-cast>RayCast projects a line segment from the node’s world position to position + direction. Each frame the collision system checks whether any registered collider in the collidesWith groups intersects the ray. When the first intersecting collider changes, colliderEntered and colliderExited fire.
import { useNode, useEvent } from 'fraxel/hooks'
import { PrimaryNode, Vector2 } from 'fraxel'

function Detector() {
  const ray = useNode(PrimaryNode.RayCast)

  useEvent(ray, 'colliderEntered', (collider) => {
    console.log('Raycast hit:', collider.group)
  })

  useEvent(ray, 'colliderExited', () => {
    console.log('Raycast cleared')
  })

  return (
    <ray-cast
      ref={ray}
      direction={new Vector2(200, 0)}
      collidesWith={['enemy', 'wall']}
    />
  )
}

Props

direction
Vector2 | VectorLike | Reactive<VectorLike>
required
The relative endpoint from the ray’s origin. A Vector2(100, 0) casts 100 px to the right. The length of this vector determines how far the ray reaches.
collidesWith
string[]
required
Groups to detect. Only colliders belonging to these groups will trigger events.
position
VectorLike | Reactive<VectorLike>
Local offset of the ray’s origin. Defaults to Vector2.ZERO.

Events

EventuseEvent nameCallbackDescription
colliderEntered'colliderEntered'(collider: Collider) => voidFires when the first collider the ray hits changes (or a new one is detected).
colliderExited'colliderExited'(collider: Collider) => voidFires when the previously detected collider is no longer in range.

Methods

MethodSignatureDescription
getCollider() => Collider | nullReturns the currently detected collider, or null if the ray hits nothing.

Instance properties

PropertyTypeDescription
directionVector2The current direction vector. Mutable.
collidesWithstring[]Groups the ray detects (read-only).
lengthnumberEuclidean length of the direction vector (read-only).
Enable gameConfig.testOptions.showRayCasts = true to visualize ray directions during development.
Class: Clickable · Enum: PrimaryNode.Clickable · JSX tag: <clickable>Clickable performs rectangular hit-testing against the pointer each frame. It exposes four interaction events: enter, exit, click (pointer-up inside area), and mouseOver (every frame while hovered). Callback props (onClick, onMouseEnter, onMouseExit, onMouseOver) are convenience shorthand for the equivalent useEvent subscriptions.
import { useNode, useEvent } from 'fraxel/hooks'
import { PrimaryNode } from 'fraxel'

function Button() {
  const btn = useNode(PrimaryNode.Clickable)

  useEvent(btn, 'clicked', (pos) => {
    console.log('Clicked at local position', pos.x, pos.y)
  })

  useEvent(btn, 'mouseEntered', () => {
    console.log('Hover start')
  })

  return (
    <sprite textureId={BUTTON_TEXTURE}>
      <clickable ref={btn} size={[96, 32]} />
    </sprite>
  )
}

Props

size
VectorLike | Reactive<VectorLike>
required
Dimensions of the hit area [width, height]. There is no default — size must always be provided.
disabled
boolean | Reactive<boolean>
When true, all interaction events are suppressed. Defaults to false.
position
VectorLike | Reactive<VectorLike>
Local offset of the hit area’s top-left corner. Defaults to Vector2.ZERO.

Callback props (shorthand)

These JSX props attach listeners directly without useEvent:
PropSignatureDescription
onClick(position: Vector2) => voidFired on pointer-up inside the area.
onMouseEnter() => voidFired when the pointer enters the area.
onMouseExit() => voidFired when the pointer leaves the area.
onMouseOver(position: Vector2) => voidFired every frame while the pointer is inside the area.

Events (useEvent)

EventuseEvent nameCallbackDescription
clicked'clicked'(position: Vector2) => voidPointer released inside the hit area. Position is local to the node’s top-left corner.
mouseEntered'mouseEntered'() => voidPointer entered the hit area.
mouseExited'mouseExited'() => voidPointer left the hit area.
mouseOver'mouseOver'(position: Vector2) => voidEvery frame while the pointer is inside the area. Position is local.

Instance properties

PropertyTypeDescription
sizeVector2Current hit area dimensions.
disabledbooleanWhether interaction is suppressed.
Enable gameConfig.testOptions.showClickables = true to visualize hit areas during development. Disabled areas render in a muted yellow; active areas in a bright yellow.
Class: RigidBody · Enum: PrimaryNode.RigidBody · JSX tag: <rigid-body>RigidBody adds physics simulation — gravity, velocity integration, and collision response — to a sibling <collider>. It must be placed as a sibling (not a child) of a Collider node inside the same parent. Gravity defaults to 980 px/s² downward. Use physicsBody to apply forces and impulses programmatically.For full physics documentation see the Physics guide.
import { useNode, useMount } from 'fraxel/hooks'
import { PrimaryNode, shapes } from 'fraxel'

function FallingRock() {
  const rb = useNode(PrimaryNode.RigidBody)

  useMount(() => {
    rb.node.physicsBody.applyImpulse(new Vector2(50, -200))
  })

  return (
    <transform position={[300, 0]}>
      <sprite textureId={ROCK_TEXTURE} />
      <collider
        shape={shapes.circle(16)}
        group={['rock']}
        collidesWith={['ground', 'wall']}
      />
      <rigid-body ref={rb} mass={2} bounce={0.5} friction={0.3} />
    </transform>
  )
}

Props

mass
number
Mass of the physics body. Heavier bodies are less affected by forces. 0 means infinite mass (behaves like static). Defaults to 1.
friction
number
Friction coefficient 01. 0 = frictionless, 1 = maximum friction. Defaults to 0.1.
bounce
number
Restitution / bounce coefficient 01. 0 = no bounce, 1 = perfectly elastic. Defaults to 0.
isStatic
boolean
When true, the body never moves regardless of forces. Use for immovable terrain. Defaults to false.
useGravity
boolean
When false, gravity is not applied to this body. Defaults to true.

Instance properties

PropertyTypeDescription
physicsBodyPhysicsBodyThe underlying physics body. Use this to apply forces and impulses.

PhysicsBody methods

MethodSignatureDescription
applyForce(force: Vector2) => voidApplies a continuous force each frame (acceleration-based).
applyImpulse(impulse: Vector2) => voidApplies an instant velocity change.
setVelocity(velocity: Vector2) => voidDirectly sets the body’s velocity vector.
<rigid-body> must be a sibling of <collider> inside the same parent node. Nesting it as a child of the collider will not work.
Class: Camera · Enum: PrimaryNode.Camera · JSX tag: <camera>Camera controls the viewport by translating and scaling the canvas context around a target. Place the <camera> at the root of your scene and nest all game-world content inside it. Children are rendered in the camera’s transformed space. Use follow(target) to track a moving node.For full camera documentation see the Camera guide.
import { useNode, useMount } from 'fraxel/hooks'
import { PrimaryNode } from 'fraxel'

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

  useMount(() => {
    // Track the player sprite
    camera.node.follow(player.node)
  })

  return (
    <camera ref={camera} zoom={1.5}>
      <sprite textureId={BG_TEXTURE} />
      <sprite ref={player} textureId={PLAYER_TEXTURE} position={[400, 300]} />
    </camera>
  )
}

Props

zoom
number | Reactive<number>
Viewport scale factor. 1 = no zoom, 2 = everything appears twice as large, 0.5 = zoomed out. Defaults to 1.
position
VectorLike | Reactive<VectorLike>
Camera position in world space. Used when not following a target. Defaults to Vector2.ZERO.

Methods

MethodSignatureDescription
follow(target: Node2D) => voidMakes the camera center on target’s world position each frame.
follow(target: undefined) => voidStops following; camera returns to its position prop.

Instance properties

PropertyTypeDescription
zoomnumberGet/set the current zoom level.

Events

Inherits all shared Node2D events: started, updated, drawed, destroyed.

Build docs developers (and LLMs) love