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
| Parameter | Type | Description |
|---|
sprite | Sprite | The Sprite instance to animate |
textureId | symbol | null | undefined | Texture symbol from loadTexture(). Pass null to keep the sprite’s current texture unchanged |
columns | number | Number of columns in the sheet grid. Defaults to 1 |
rows | number | Number 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
| Parameter | Type | Description |
|---|
sprite | Sprite | NodeReference | A Sprite instance or a NodeReference to a sprite node |
textureId | symbol | null | undefined | Texture symbol. Pass null to keep the current texture; undefined uses the sprite’s texture |
columns | number | Number of columns in the sheet grid. Defaults to 1 |
rows | number | Number 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 |
duration | number | Total animation time in seconds. FPS = (columns * rows) / duration |
loop | boolean | Whether 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
| Prop | Type | Default | Description |
|---|
animations | () => Record<string, Animation> | — | Factory function returning animation definitions. Called when the node starts, not at construction time |
currentAnim | string | SignalGetter<string> | — | Initial animation name, or a reactive getter that switches animations automatically |
destroyOnEnd | boolean | false | Destroys 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
| Property | Type | Description |
|---|
target | T | The object whose property will be interpolated |
prop | keyof T | Property name to animate |
from | number | Start value |
to | number | End value |
duration | number | Duration in seconds |
easing | EasingFn | Easing function. Defaults to linear |
onUpdate | (value: number) => void | Called every frame with the current interpolated value |
onComplete | () => void | Called 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'
| Function | Description |
|---|
linear | No easing — constant speed |
easeInQuad | Slow start, accelerates (quadratic) |
easeOutQuad | Fast start, decelerates (quadratic) |
easeInOutQuad | Slow start and end (quadratic) |
easeInCubic | Cubic acceleration |
easeOutCubic | Cubic deceleration |
easeInOutCubic | Cubic slow start and end |
easeBackOut | Overshoots then settles back |
easeBackInOut | Overshoots in both directions |
easeOutBounce | Bounces at the end |
easeInBounce | Bounces at the start |
easeOutElastic | Spring-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.