Animation in PixiJS

Updated June 2026

PixiJS supports three core animation approaches: frame-by-frame animation with AnimatedSprite for sprite sheet playback, property tweening for smooth transitions of position, scale, rotation, and alpha, and skeletal animation through Spine integration for complex characters with blended states. Each technique serves different needs, and most games combine all three to create polished, responsive animation.

Animation in a game is movement, change, and responsiveness. A character walks, a button pulses when hovered, a coin spins as it is collected, particles scatter from an explosion. PixiJS provides the tools to implement all of these, from simple sprite sheet playback to sophisticated skeletal animation with blend trees. This guide covers each technique with practical details so you can apply them in your own projects.

Step 1: Frame-by-Frame Animation with AnimatedSprite

AnimatedSprite is PixiJS's built-in class for playing through a sequence of texture frames, the most common animation technique in 2D games. You provide an array of Texture objects representing each frame of the animation, and AnimatedSprite cycles through them at a configurable speed.

To create an AnimatedSprite, first load a sprite sheet through the Assets class. The sprite sheet's JSON descriptor defines named frames that PixiJS parses into individual Texture objects. Collect the frames you need into an array, then create the animated sprite: const walkAnim = new AnimatedSprite(walkFrames).

Control playback speed with the animationSpeed property, which determines how many frames advance per ticker update. A value of 0.15 means roughly 9 frames per second at 60 FPS, which works well for character walk cycles. Higher values play faster, lower values play slower. Setting loop = false makes the animation play once and stop, useful for one-shot effects like explosions or attack swings.

AnimatedSprite fires an onComplete callback when a non-looping animation finishes its last frame. This is essential for chaining game logic to animation timing, such as dealing damage at the end of an attack animation, removing a destroyed enemy after its death animation plays, or transitioning back to the idle animation after a one-shot action completes.

You can switch between animations dynamically by changing the sprite's textures array and calling play() again. A character controller might maintain arrays for idle, walk, run, jump, and attack animations, swapping them based on the current state. Stop the current animation with stop(), assign new textures, reset the current frame to 0, and play the new animation.

For efficiency, AnimatedSprite shares the same base textures as regular sprites. If all your character frames are packed into one sprite sheet atlas, the AnimatedSprite benefits from the same batching optimization as static sprites, adding no extra draw calls regardless of which frame is currently displayed.

Step 2: Property Tweening for Smooth Motion

Tweening (short for "in-betweening") smoothly interpolates a property from one value to another over a duration. While AnimatedSprite changes the image shown, tweening changes numeric properties like position, scale, rotation, and alpha to create smooth, continuous motion.

PixiJS does not include a built-in tweening library, but several excellent options integrate easily. GSAP (GreenSock Animation Platform) is the most popular, offering a powerful timeline system, easing functions, and sequencing capabilities. Tween.js is a simpler, lighter alternative. Both libraries work by updating JavaScript object properties on each frame, which PixiJS then renders automatically.

A basic tween moves a sprite from its current position to a target position over a set duration. For example, using GSAP: gsap.to(sprite, { x: 400, y: 200, duration: 1, ease: 'power2.out' }). This moves the sprite to (400, 200) over one second with a smooth deceleration curve. PixiJS renders the sprite at its updated position each frame without any additional code.

Easing functions control the acceleration curve of the tween. Linear easing moves at constant speed, which looks mechanical and unnatural. Ease-out starts fast and decelerates, creating a settling feel. Ease-in starts slow and accelerates, useful for objects launching. Ease-in-out combines both for smooth start and stop. Bounce and elastic easings add character to UI animations and collectible items.

Tweening is ideal for UI animations (buttons scaling on hover, panels sliding in), collectible effects (coins floating up and fading), camera movements (smooth panning to a new focus point), and any motion that follows a predictable path over a known duration. For physics-driven movement where the path is unpredictable, use the ticker with velocity calculations instead.

You can also tween custom properties. If your game objects have a "health" property displayed as a bar width, tweening that property creates smooth health bar transitions. Any numeric property on any JavaScript object can be tweened, not just PixiJS display object properties.

Step 3: Skeletal Animation with Spine

For characters with many animations, complex body part hierarchies, and smooth transitions between states, skeletal animation is far more efficient than frame-by-frame sprite sheets. Instead of storing every frame as a separate image, skeletal animation defines a skeleton of bones and skin meshes, then animates by transforming the bones.

Spine is the most widely used skeletal animation tool for 2D games, and pixi-spine provides the official runtime for rendering Spine animations in PixiJS. Artists create animations in the Spine editor, export a JSON data file and a texture atlas, and the pixi-spine runtime loads and plays these animations in the PixiJS scene graph.

The advantages of skeletal animation over sprite sheets are substantial. A character with 20 animations at 12 frames each would require 240 individual frame images in a sprite sheet. With Spine, the same character needs only the skin mesh textures (typically 1-3 images) plus a small JSON file defining all bone transforms. This reduces both download size and GPU memory consumption dramatically.

Spine also supports animation mixing, which blends between two animations smoothly over a transition duration. A character moving from walk to run does not snap between the two states but smoothly interpolates bone positions between the walk pose and the run pose. This creates polished, natural-looking transitions that would require extensive hand-drawn in-between frames with traditional sprite animation.

Additional Spine features include inverse kinematics (bones automatically adjust to keep a foot on the ground or a hand on a weapon), mesh deformation for squash and stretch effects, and events embedded in animation timelines that trigger sounds or gameplay logic at specific frames. These features are exposed through the pixi-spine API, giving your game code full control over animation playback, speed, mixing, and event handling.

DragonBones is an alternative skeletal animation tool with its own PixiJS runtime (dragonbones-pixi). It offers similar features to Spine and is fully open-source, making it a viable option for developers who want skeletal animation without Spine's license cost.

Step 4: Custom Animation with the Ticker

The PixiJS Ticker provides the frame loop that drives all animation, and many effects are best implemented as custom update logic rather than through AnimatedSprite or tweening libraries.

Register an update function with app.ticker.add((ticker) => { ... }). This function runs on every frame and receives the ticker object with deltaTime for frame-rate-independent calculations. Multiply any per-frame change by deltaTime to ensure consistent speed across different frame rates.

Common ticker-driven animations include oscillating motion (a floating item bobbing up and down using sine waves), continuous rotation (a spinning coin or loading indicator), velocity-based movement (a projectile flying in a direction at a speed), and procedural effects (pulsing glow, screen shake, wave distortion).

For oscillating motion, use a time accumulator: increment a variable by deltaTime each frame, then set the sprite's y position using Math.sin(time * frequency) * amplitude. This creates smooth, repeating vertical motion without any tweening library. Adjust frequency and amplitude to control the speed and range of the oscillation.

Screen shake is another common ticker-driven effect. When triggered, apply random small offsets to the camera or stage container's position each frame for a set duration, then reset to the original position. The randomness creates an impact feel that is difficult to achieve with a predefined tween path.

The ticker is also where you update particle systems, interpolate between physics states for smooth rendering, and drive any animation that depends on game state rather than following a predetermined path.

Step 5: Combining Animation Techniques

Polished games rarely use a single animation technique in isolation. A typical character might use AnimatedSprite for its walk and attack frame sequences, property tweens for smooth position changes when knocked back, Spine skeletal animation for complex combo attacks with mesh deformation, and ticker-driven procedural effects for breathing idle motion or hit flash overlay.

The key to combining techniques is separating concerns. Let each system manage its own responsibility without conflicting with the others. AnimatedSprite controls which frame is displayed. Tweens or the game loop control position and scale. Spine controls bone-driven mesh animation. Ticker callbacks handle procedural effects like color flashing (tint changes) or screen shake.

State machines help coordinate these systems. Define states like idle, walking, attacking, hurt, and dead. Each state specifies which AnimatedSprite animation plays, which tweens activate, and which ticker behaviors run. Transitions between states trigger animation changes and cleanup, ensuring no conflicting animations fight for control of the same properties.

Performance remains important when layering multiple animation systems. AnimatedSprite is essentially free since it only changes which texture frame is used. Tweening adds minimal overhead for a few dozen simultaneous tweens. Spine is more expensive due to bone transform calculations and mesh updates, so limit the number of simultaneously visible Spine characters to what your target hardware can handle. Profile regularly during development to catch animation performance issues early.

Key Takeaway

Use AnimatedSprite for frame-by-frame playback from sprite sheets, tweening libraries for smooth property transitions, Spine for complex character animation, and the Ticker for procedural effects. Combining all four techniques gives you the full range of motion a polished 2D game requires.