Skip to main content
Frames are the individual steps in an animation. Each frame represents a moment in time, and when played in sequence, frames create the illusion of movement.

What are Frames?

A frame is a single image in an animation sequence. In Aseprite, frames are numbered starting from 1, and each frame can have:
  • A duration (how long it displays)
  • Cels (the actual image content from various layers)
  • A position in the animation timeline
Every sprite starts with at least one frame. You can add more frames to create animations.

Frame Basics

Accessing Frames

local sprite = Sprite(32, 32)

-- Access frames array
print(#sprite.frames)  -- 1 (default)

-- Get specific frame
local frame = sprite.frames[1]
print(frame.frameNumber)  -- 1

-- Iterate through frames
for i, frame in ipairs(sprite.frames) do
  print("Frame", frame.frameNumber)
end

Frame Properties

local sprite = Sprite(32, 32)
local frame = sprite.frames[1]

-- Frame number (1-based)
print(frame.frameNumber)  -- 1

-- Duration (in seconds)
print(frame.duration)  -- 0.1 (100ms default)
frame.duration = 0.2   -- Set to 200ms

-- Navigate between frames
print(frame.previous)  -- nil (first frame)
print(frame.next)      -- nil (only one frame)

Creating Frames

Adding New Frames

-- Create new empty frame (no cels copied)
local sprite = Sprite(32, 32)
local newFrame = sprite:newEmptyFrame()

print(newFrame.frameNumber)  -- 2
print(#sprite.frames)        -- 2

-- No cels in the new frame
print(sprite.layers[1]:cel(newFrame))  -- nil

Inserting Frames

You can insert frames at specific positions:
local sprite = Sprite(32, 32)
sprite:newFrame()  -- Frame 2
sprite:newFrame()  -- Frame 3

print(#sprite.frames)  -- 3

-- Insert empty frame at position 2
local inserted = sprite:newEmptyFrame(2)
print(inserted.frameNumber)  -- 2
print(#sprite.frames)         -- 4

-- Insert frame with content at position 2
local inserted2 = sprite:newFrame(2)

Deleting Frames

local sprite = Sprite(32, 32)
sprite:newFrame()
sprite:newFrame()
print(#sprite.frames)  -- 3

-- Delete specific frame
sprite:deleteFrame(2)
print(#sprite.frames)  -- 2

-- Delete by frame object
local frame = sprite.frames[1]
sprite:deleteFrame(frame)
Be careful when storing frame references - if frames are added or deleted, the frame numbers change, but old frame objects still reference their original frame number.

Frame Duration

Frame duration controls how long each frame displays during playback.

Individual Frame Duration

local sprite = Sprite(32, 32)
local frame = sprite.frames[1]

-- Get duration in seconds
print(frame.duration)  -- 0.1 (100ms default)

-- Set duration
frame.duration = 0.05   -- 50ms (faster)
frame.duration = 0.5    -- 500ms (slower)

Bulk Duration Operations

local sprite = Sprite(32, 32)
for i = 1, 5 do
  sprite:newFrame()
end

-- Set duration for range of frames
sprite:setFrameRangeDuration(1, 3, 0.1)  -- Frames 1-3: 100ms
sprite:setFrameRangeDuration(4, 6, 0.2)  -- Frames 4-6: 200ms

-- Set same duration for all frames
sprite:setDurationForAllFrames(0.15)  -- All frames: 150ms

Total Animation Duration

local sprite = Sprite(32, 32)
sprite.frames[1].duration = 0.1
sprite:newFrame()
sprite.frames[2].duration = 0.2
sprite:newFrame()
sprite.frames[3].duration = 0.15

-- Get total duration
local total = sprite:totalAnimationDuration()
print(total)  -- 0.45 (450ms total)

Working with Cels

Cels are the actual image content that appears in frames. Each layer can have one cel per frame.

Accessing Cels

local sprite = Sprite(32, 32)
local layer = sprite.layers[1]

-- Get cel at specific frame
local cel = layer:cel(1)  -- Frame 1
if cel then
  print("Found cel at frame 1")
  print(cel.image.width)  -- Access image
  print(cel.position)     -- Cel position
end

-- Create new cel
sprite:newEmptyFrame(2)  -- Frame 2 has no cels
sprite:newCel(layer, 2)  -- Create cel at frame 2

Linked Cels

Multiple frames can share the same cel (image data):
local sprite = Sprite(32, 32)
local layer = sprite.layers[1]

-- Create linked cel
sprite:newEmptyFrame(2)
local cel1 = layer:cel(1)
local linkedCel = sprite:newCel(layer, 2, cel1.image)

-- Check if cels are linked
print(cel1.image == linkedCel.image)  -- true (same image)

-- Editing one affects the other
cel1.image:clear(Color(255, 0, 0))
-- linkedCel.image is also red now
Linked cels save memory and ensure consistency when you want multiple frames to show the same image.

Frame Navigation

Frames maintain references to adjacent frames:
local sprite = Sprite(32, 32)
sprite:newFrame()  -- Frame 2
sprite:newFrame()  -- Frame 3

local frame2 = sprite.frames[2]

-- Navigate to neighbors
local prev = frame2.previous
print(prev.frameNumber)  -- 1

local next = frame2.next
print(next.frameNumber)  -- 3

-- First and last frames
if frame2.previous ~= nil then
  print("Not the first frame")
end

if frame2.next ~= nil then
  print("Not the last frame")
end

Frame Count

local sprite = Sprite(32, 32)

-- Total frames
print(sprite:totalFrames())  -- 1
print(#sprite.frames)        -- 1 (same)

-- Last frame index
print(sprite:lastFrame())    -- 0 (0-indexed internally)

-- Add frames
for i = 1, 5 do
  sprite:newFrame()
end
print(sprite:totalFrames())  -- 6

Animation Tags

Frames can be organized into named animation sequences using tags. See Animation for details.
local sprite = Sprite(32, 32)
for i = 1, 8 do
  sprite:newFrame()
end

-- Create animation tag
local walkTag = sprite:newTag(1, 4)
walkTag.name = "walk"

local jumpTag = sprite:newTag(5, 8)
jumpTag.name = "jump"

print(walkTag.frames)  -- 4 (frames 1-4)

Best Practices

Before animating, decide how many frames your animation needs:
  • Simple animations: 2-4 frames
  • Walk cycles: 6-8 frames
  • Complex animations: 12+ frames
More frames create smoother animations but require more work.
Keep frame duration consistent within an animation sequence for smooth playback. Vary duration intentionally for timing effects.
-- Consistent timing for walk cycle
sprite:setFrameRangeDuration(1, 8, 0.1)

-- Special timing for impact frame
sprite.frames[5].duration = 0.2  -- Hold this frame longer
If part of your sprite doesn’t change (like a background layer), use linked cels to save memory and ensure consistency.
Use tags to organize frames into named animations like “idle”, “walk”, “jump”. This makes it easier to manage and export animations.

Common Patterns

Create Frame Sequence

function createFrameSequence(sprite, count)
  for i = 1, count do
    sprite:newFrame()
  end
end

local sprite = Sprite(32, 32)
createFrameSequence(sprite, 7)
print(#sprite.frames)  -- 8 (1 original + 7 new)

Set Consistent Timing

function setAnimationTiming(sprite, fps)
  local duration = 1.0 / fps
  sprite:setDurationForAllFrames(duration)
end

local sprite = Sprite(32, 32)
createFrameSequence(sprite, 7)
setAnimationTiming(sprite, 10)  -- 10 FPS animation

Reverse Frame Order

function createPingPongFrames(sprite, startFrame, endFrame)
  -- Creates frames in reverse for ping-pong effect
  for i = endFrame - 1, startFrame + 1, -1 do
    local frame = sprite:newFrame()
    -- Copy cel from frame i
    for _, layer in ipairs(sprite.layers) do
      local cel = layer:cel(i)
      if cel then
        sprite:newCel(layer, frame.frameNumber, cel.image)
      end
    end
  end
end

Frame Iteration

local sprite = Sprite(32, 32)
for i = 1, 5 do
  sprite:newFrame()
end

-- Iterate with index
for i, frame in ipairs(sprite.frames) do
  print("Frame", i, "duration:", frame.duration)
end

-- Process every other frame
for i = 1, #sprite.frames, 2 do
  local frame = sprite.frames[i]
  frame.duration = 0.2
end

Animation

Learn about creating animations with frames

Layers

Understand how layers contain cels at frames

Sprites

Learn about the sprite container

API Reference

For complete API documentation, see: