Skip to content

Animation

three-flatland supports frame-based animation using spritesheets. The AnimatedSprite2D class works with SpriteSheetLoader to load and play animations.

import { AnimatedSprite2D, SpriteSheetLoader, Layers } from 'three-flatland';
// Load spritesheet (texture presets automatically applied)
const spriteSheet = await SpriteSheetLoader.load('/sprites/character.json');
// Create animated sprite
const sprite = new AnimatedSprite2D({
spriteSheet,
animationSet: {
fps: 10, // Default fps for all animations
animations: {
idle: {
frames: ['idle_0', 'idle_1', 'idle_2', 'idle_3'],
fps: 8,
loop: true,
},
run: {
frames: ['run_0', 'run_1', 'run_2', 'run_3'],
fps: 12,
loop: true,
},
jump: {
frames: ['jump_0', 'jump_1', 'jump_2'],
fps: 10,
loop: false,
},
},
},
animation: 'idle', // Start with idle animation
layer: Layers.ENTITIES,
});
sprite.scale.set(64, 64, 1);
scene.add(sprite);
// In your animation loop
function animate() {
const deltaMs = /* time since last frame */;
sprite.update(deltaMs);
renderer.render(scene, camera);
}

Animation Set Definition

The animationSet option provides a structured way to define multiple animations:

animationSet: {
fps: 12, // Default fps (optional)
animations: {
idle: {
frames: ['frame_0', 'frame_1', 'frame_2'], // Frame names from spritesheet
fps: 8, // Override default fps (optional)
loop: true, // Loop animation (default: true)
pingPong: false, // Play forward then backward (optional)
},
attack: {
frames: ['attack_0', 'attack_1', 'attack_2'],
fps: 15,
loop: false,
},
},
}

Playback Control

sprite.play('run'); // Play animation by name
sprite.pause(); // Pause at current frame
sprite.resume(); // Resume paused animation
sprite.stop(); // Stop and reset
sprite.gotoFrame(3); // Jump to specific frame
// Check animation state
sprite.isPlaying(); // Is any animation playing?
sprite.isPlaying('run'); // Is 'run' animation playing?
sprite.currentAnimation; // Get current animation name

Playback Speed

sprite.speed = 1.5; // 1.5x speed
sprite.speed = 0.5; // Half speed
sprite.speed = 2; // Double speed

Animation Callbacks

Use the play() method’s options to handle animation events:

sprite.play('attack', {
onFrame: (frameIndex) => {
console.log('Frame:', frameIndex);
if (frameIndex === 2) {
// Trigger damage on specific frame
dealDamage();
}
},
onComplete: () => {
// Animation finished (non-looping only)
sprite.play('idle');
},
});

Loading Spritesheets

The SpriteSheetLoader supports Aseprite JSON format. Texture presets (like NearestFilter for pixel art) are automatically applied:

import { SpriteSheetLoader } from 'three-flatland';
// Load spritesheet (presets automatically applied)
const spriteSheet = await SpriteSheetLoader.load('/sprites/character.json');
// Access frame data
const frame = spriteSheet.getFrame('idle_0');
console.log(frame); // { x, y, width, height, sourceWidth, sourceHeight, ... }

Update Loop

The update(deltaMs) method must be called each frame to advance the animation:

let lastTime = performance.now();
function animate() {
const now = performance.now();
const deltaMs = now - lastTime;
lastTime = now;
// Update all animated sprites
sprite.update(deltaMs);
renderer.render(scene, camera);
requestAnimationFrame(animate);
}