Documentation Index
Fetch the complete documentation index at: https://mintlify.com/sanchedev/tiny-engine/llms.txt
Use this file to discover all available pages before exploring further.
tiny-engine animates sprites by generating keyframes directly from sprite sheet textures. Instead of writing per-frame offsets by hand, you describe the grid layout of your sheet and let the keyframe helpers do the math — then hand the result to an <animation-player> node that drives playback every frame.
Keyframe helper functions
Three helpers from tiny-engine let you build keyframe arrays that the AnimationPlayer understands.
kfFromSpriteSheet
Generates keyframes for every cell in a sprite sheet grid, optionally restricted to a sub-range of frames.
import { kfFromSpriteSheet } from 'tiny-engine'
kfFromSpriteSheet(sprite, textureId, spritesCountX, spritesCounty, range?)
| Parameter | Type | Description |
|---|
sprite | Sprite | The sprite instance to animate. |
textureId | symbol | null | Texture symbol. Pass null to keep the sprite’s current texture and only update the frame margin. |
spritesCountX | number | Number of columns in the sheet. Defaults to 1. |
spritesCounty | number | Number of rows in the sheet. Defaults to 1. |
range | [number, number] | Optional [from, to] tuple (0-indexed, inclusive) to select a subset of frames. |
kfFromSpriteSheet sets sprite.sourceSize automatically from the computed cell dimensions, so you do not need to set it yourself.
kfFromProp
Returns a single keyframe that sets one property on any Node instance.
import { kfFromProp } from 'tiny-engine'
kfFromProp(node, property, value)
// e.g.
kfFromProp(sprite, 'textureId', WALK_TEXTURE)
kfFromProp(sprite, 'margin', new Vector2(32, 0))
| Parameter | Type | Description |
|---|
node | T extends Node | Any node instance. |
property | K extends keyof T | The property name to set. |
value | T[K] | The value to assign on every tick of that frame. |
multiKF
Combines several keyframes into one so they all execute at the same animation step.
import { multiKF } from 'tiny-engine'
multiKF([frameA, frameB, frameC]) // returns a single AnimationKeyframe
| Parameter | Type | Description |
|---|
kfs | AnimationKeyframe[] | Array of keyframes to run simultaneously. |
Generating walk and idle frames from a sheet
The example below uses a single 4×2 sprite sheet where the top row holds idle frames and the bottom row holds walk frames.
import { kfFromSpriteSheet } from 'tiny-engine'
import { useRefNode } from 'tiny-engine/hooks'
import { PrimaryNode } from 'tiny-engine'
const SHEET = Symbol('character-sheet')
function CharacterSprite() {
const sprite = useRefNode(PrimaryNode.Sprite)
// All 8 cells — used to derive idle and walk subsets
// Rows: 0-3 idle (first row), 4-7 walk (second row)
const idleFrames = kfFromSpriteSheet(sprite.node, SHEET, 4, 2, [0, 3])
const walkFrames = kfFromSpriteSheet(sprite.node, SHEET, 4, 2, [4, 7])
return (
<sprite ref={sprite} textureId={SHEET} sourceSize={new Vector2(16, 16)}>
<animation-player
animations={() => ({
idle: { keyframes: idleFrames, fps: 6, loop: true },
walk: { keyframes: walkFrames, fps: 8, loop: true },
})}
currentAnim="idle"
/>
</sprite>
)
}
The range tuple is 0-indexed and inclusive on both ends. For a 4×2 sheet, frames 0–3 are the first row and frames 4–7 are the second row.
The <animation-player> node
| Prop | Type | Description |
|---|
animations | () => Record<string, Animation> | Deferred function returning a map of animation names to Animation objects. |
currentAnim | string | SignalGetter<string> | The animation to play. Accepts a static name or a reactive signal getter. |
destroyOnEnd | boolean | When true, the node destroys itself when the current non-looping animation ends. Defaults to false. |
ref | NodeReference<PrimaryNode.AnimationPlayer> | Optional ref to access the node imperatively. |
Each Animation object has the shape:
interface Animation {
fps: number // Frames per second
keyframes: AnimationKeyframe[]
loop?: boolean // Whether to loop (default false)
}
Reactive animation switching with useComputed
When currentAnim receives a SignalGetter<string>, the player subscribes to signal changes and calls .play() automatically whenever the value changes. No manual wiring required.
import { useSignal, useComputed } from 'tiny-engine/hooks'
function Hero() {
const sprite = useRefNode(PrimaryNode.Sprite)
const [isWalking, setIsWalking] = useSignal(false)
// Derived signal: resolves to 'walk' or 'idle' reactively
const animName = useComputed(() => (isWalking() ? 'walk' : 'idle'))
return (
<sprite ref={sprite} textureId={SHEET} sourceSize={new Vector2(16, 16)}>
<animation-player
animations={() => ({
idle: { keyframes: idleFrames, fps: 6, loop: true },
walk: { keyframes: walkFrames, fps: 8, loop: true },
})}
currentAnim={animName}
/>
</sprite>
)
}
When isWalking flips to true, animName re-evaluates to 'walk' and the player switches immediately.
Why animations is a deferred function
The animations prop is intentionally a function rather than a plain object. The AnimationPlayer constructor is called synchronously during JSX evaluation — at that moment, child sprite nodes may not yet exist in the scene tree, so calling sprite.node on a ref would return undefined.
By wrapping the animation definitions in a function, tiny-engine defers the call until the node’s started event fires. By that point, all child nodes are guaranteed to have been initialised and sprite.node resolves correctly.
// ✅ Correct — evaluated after all nodes start
animations={() => ({
idle: { keyframes: kfFromSpriteSheet(sprite.node, SHEET, 4, 2), fps: 6 },
})}
// ❌ Wrong — sprite.node is undefined at construction time
animations={{
idle: { keyframes: kfFromSpriteSheet(sprite.node, SHEET, 4, 2), fps: 6 },
}}
Grab a ref on the <animation-player> and listen to its animationEnded event with useEvent to trigger one-shot effects or chain animations. Use setNext(animName) to queue the next animation imperatively.const anim = useRefNode(PrimaryNode.AnimationPlayer)
useEvent(anim, 'animationEnded', (finishedAnim) => {
if (finishedAnim === 'attack') {
anim.node.play('idle')
}
})