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.

Fraxel’s animation system covers two complementary areas: frame-based sprite sheet animation driven by the AnimationPlayer node, and property interpolation through the tweening API. Sprite animation lets you slice a packed texture into frames and play them back at a set frame rate, while tweening lets you smoothly interpolate any numeric property — position, opacity, scale — over time using one of twelve built-in easing functions. Both systems are imported from the fraxel/animation subpackage and work naturally alongside Fraxel’s reactivity model.

Sprite Sheet Keyframes

The low-level building blocks for sprite animation are keyframesFromSheet, kfFromProp, and multiKF. Together they generate the AnimationKeyframe[] arrays that AnimationPlayer consumes.
import { keyframesFromSheet, kfFromProp, multiKF } from 'fraxel'

// All frames of a 4-column, 1-row sheet
const allFrames = keyframesFromSheet(spriteNode, walkTexture, 4, 1)

// Subset: frames 0–3 from a 4×2 sheet (first row)
const walkFrames = keyframesFromSheet(spriteNode, walkTexture, 4, 2, [0, 3])

// Subset: frames 4–7 (second row)
const runFrames  = keyframesFromSheet(spriteNode, walkTexture, 4, 2, [4, 7])

// Manual keyframe: set a single property on one frame
const frame1 = kfFromProp(spriteNode, 'textureId', frame1Texture)

// Combine multiple keyframes into one (execute in the same frame)
const combined = multiKF([frame1, frame2, frame3])

keyframesFromSheet parameters

ParameterTypeDescription
spriteSpriteThe Sprite instance to animate
textureIdsymbol | null | undefinedTexture symbol from loadTexture(). Pass null to keep the sprite’s current texture unchanged
columnsnumberNumber of columns in the sheet grid. Defaults to 1
rowsnumberNumber of rows in the sheet grid. Defaults to 1
range[number, number]Optional [from, to] tuple (0-indexed, inclusive) to select a subset of frames

kfFromProp

Sets any node property at a specific frame:
import { kfFromProp } from 'fraxel'

// Sets sprite.textureId = IDLE_TEXTURE on this keyframe
const kf = kfFromProp(sprite, 'textureId', IDLE_TEXTURE)

multiKF

Merges several keyframes so they all execute at the same animation frame — useful when you need to set both a texture and a margin offset simultaneously:
import { multiKF, kfFromProp } from 'fraxel'
import { Vector2 } from 'fraxel'

const firstFrame = multiKF([
  kfFromProp(sprite, 'textureId', WALK_TEXTURE),
  kfFromProp(sprite, 'margin', new Vector2(0, 0)),
])

animationFromSheet

animationFromSheet is a higher-level helper that wraps keyframesFromSheet and automatically calculates the fps value from the total frame count and desired duration. It returns a complete Animation object ready to pass directly to AnimationPlayer.
import { animationFromSheet } from 'fraxel'

// 4-column single-row sheet, 1-second loop
const idle = animationFromSheet(sprite, IDLE_TEXTURE, {
  columns: 4,
  duration: 1,
  loop: true,
})

// Multi-row sheet with a range, 2-second loop
const walk = animationFromSheet(sprite, WALK_TEXTURE, {
  columns: 4,
  rows: 2,
  range: [0, 7],
  duration: 2,
  loop: true,
})
The FPS is calculated as (columns × rows) / duration, so a 4×2 sheet (8 frames) over 2 seconds plays at 4 FPS.

animationFromSheet parameters

ParameterTypeDescription
spriteSprite | NodeReferenceA Sprite instance or a NodeReference to a sprite node
textureIdsymbol | null | undefinedTexture symbol. Pass null to keep the current texture; undefined uses the sprite’s texture
columnsnumberNumber of columns in the sheet grid. Defaults to 1
rowsnumberNumber of rows in the sheet grid. Defaults to 1
range[number, number]Optional [from, to] tuple (0-indexed, inclusive) to select a subset of frames
durationnumberTotal animation time in seconds. FPS = (columns * rows) / duration
loopbooleanWhether the animation should loop

AnimationPlayer Node

The <animation-player> JSX node drives sprite animation. Attach it as a child of a <sprite> node and pass an animations record along with a currentAnim name.
import { useNode } from 'fraxel/hooks'
import { PrimaryNode } from 'fraxel'
import { animationFromSheet } from 'fraxel'

function Character() {
  const sprite = useNode(PrimaryNode.Sprite)
  const anim   = useNode(PrimaryNode.AnimationPlayer)

  const idle = animationFromSheet(sprite, IDLE_TEXTURE, { columns: 4, duration: 1, loop: true })
  const walk = animationFromSheet(sprite, WALK_TEXTURE, { columns: 4, duration: 0.8, loop: true })

  return (
    <sprite ref={sprite} textureId={IDLE_TEXTURE}>
      <animation-player
        ref={anim}
        animations={() => ({ idle, walk })}
        currentAnim="idle"
      />
    </sprite>
  )
}

Props

PropTypeDefaultDescription
animations() => Record<string, Animation>Factory function returning animation definitions. Called when the node starts, not at construction time
currentAnimstring | SignalGetter<string>Initial animation name, or a reactive getter that switches animations automatically
destroyOnEndbooleanfalseDestroys the node automatically when a non-looping animation finishes
The animations prop is intentionally a function, not an object literal. This defers evaluation until the node’s start lifecycle, ensuring any NodeReference values passed to keyframesFromSheet or animationFromSheet are already resolved before the keyframes are built.

Reactive currentAnim

Pass a SignalGetter<string> to currentAnim to switch animations automatically whenever the signal changes:
import { useComputed, useSignal } from 'fraxel/hooks'

function Character() {
  const sprite   = useNode(PrimaryNode.Sprite)
  const isWalking = useSignal(false)

  const animName = useComputed(() => (isWalking() ? 'walk' : 'idle'))

  return (
    <sprite ref={sprite} textureId={IDLE_TEXTURE}>
      <animation-player
        animations={() => ({ idle, walk })}
        currentAnim={animName}
      />
    </sprite>
  )
}
When currentAnim is a SignalGetter, the player subscribes to its dependencies and calls play() every time the computed value changes — no manual event wiring required.

destroyOnEnd for one-shot effects

// Explosion effect that removes itself after playing once
<animation-player
  animations={() => ({ explode: { keyframes: explodeFrames, fps: 12 } })}
  currentAnim="explode"
  destroyOnEnd
/>

Tweening with tween()

tween() interpolates a numeric property on any object over a set duration. It starts immediately and returns a controller for pausing, resuming, and stopping.
import { tween, easeOutQuad } from 'fraxel/animation'

const controller = tween({
  target: sprite.node,
  prop: 'opacity',
  from: 0,
  to: 1,
  duration: 0.5,
  easing: easeOutQuad,
  onUpdate:   (value) => console.log('opacity:', value),
  onComplete: ()      => console.log('fade in done'),
})

Options

PropertyTypeDescription
targetTThe object whose property will be interpolated
propkeyof TProperty name to animate
fromnumberStart value
tonumberEnd value
durationnumberDuration in seconds
easingEasingFnEasing function. Defaults to linear
onUpdate(value: number) => voidCalled every frame with the current interpolated value
onComplete() => voidCalled once when the tween finishes

Controller

const t = tween({ ... })

t.pause()      // Pause the tween at current position
t.resume()     // Resume from current position
t.stop()       // Stop and reset progress to 0
t.isPlaying    // boolean — whether it is actively running
t.progress     // number — current progress from 0 to 1

tweenValue()

When you want to animate a value without attaching it to any particular object, use tweenValue(). It calls onUpdate each frame with the interpolated number:
import { tweenValue } from 'fraxel/animation'

tweenValue({
  from: 0,
  to: 100,
  duration: 1,
  easing: easeOutCubic,
  onUpdate: (value) => console.log(value),
})
tweenValue accepts all the same options as tween() except target and prop, and returns the same TweenController.

Easing Functions

All easing functions are exported from fraxel/animation and share the EasingFn = (t: number) => number signature, mapping a normalised progress value t (0–1) to an eased output.
import {
  linear,
  easeInQuad,
  easeOutQuad,
  easeInOutQuad,
  easeInCubic,
  easeOutCubic,
  easeInOutCubic,
  easeBackOut,
  easeBackInOut,
  easeOutBounce,
  easeInBounce,
  easeOutElastic,
} from 'fraxel/animation'
FunctionDescription
linearNo easing — constant speed
easeInQuadSlow start, accelerates (quadratic)
easeOutQuadFast start, decelerates (quadratic)
easeInOutQuadSlow start and end (quadratic)
easeInCubicCubic acceleration
easeOutCubicCubic deceleration
easeInOutCubicCubic slow start and end
easeBackOutOvershoots then settles back
easeBackInOutOvershoots in both directions
easeOutBounceBounces at the end
easeInBounceBounces at the start
easeOutElasticSpring-like elastic overshoot

Sequences & Parallel

Run multiple tweens in a defined order with sequence(), or kick them all off simultaneously with parallel().

sequence()

Each tween starts only after the previous one completes:
import { tween, sequence } from 'fraxel/animation'

sequence([
  tween({ target: sprite, prop: 'opacity',  from: 0,   to: 1,   duration: 0.5 }),
  tween({ target: sprite, prop: 'position', from: 0,   to: 100, duration: 1   }),
])

parallel()

All tweens start at the same time:
import { tween, parallel } from 'fraxel/animation'

parallel([
  tween({ target: sprite, prop: 'opacity', from: 0,   to: 1, duration: 0.5 }),
  tween({ target: sprite, prop: 'scale',   from: 0.5, to: 1, duration: 0.5 }),
])
Both functions accept a TweenController[] array. You can mix tween() and tweenValue() results freely.

Using Tweens with Hooks

The useMount hook is the natural place to start entry animations — it runs once after the node has been added to the scene:
import { useMount, useNode } from 'fraxel/hooks'
import { tween, easeOutBounce } from 'fraxel/animation'
import { PrimaryNode } from 'fraxel'

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

  useMount(() => {
    tween({
      target: sprite.node,
      prop: 'position',
      from: -50,
      to:    50,
      duration: 0.8,
      easing: easeOutBounce,
    })
  })

  return <sprite ref={sprite} textureId={COIN} />
}
You can combine sequence and useMount to chain entry animations across multiple properties:
useMount(() => {
  sequence([
    tween({ target: sprite.node, prop: 'opacity',  from: 0, to: 1,   duration: 0.3 }),
    tween({ target: sprite.node, prop: 'scale',    from: 0.8, to: 1, duration: 0.2 }),
  ])
})
For further reference see the API: Animation & Tweening page, and the Nodes (2D) reference for AnimationPlayer events such as animationEnded and animationChanged.

Build docs developers (and LLMs) love