Skip to main content
This document describes the technical specifications of the Aseprite file format (.ase/.aseprite).

References

ASE files use Intel (little-endian) byte order.
  • BYTE: An 8-bit unsigned integer value
  • WORD: A 16-bit unsigned integer value
  • SHORT: A 16-bit signed integer value
  • DWORD: A 32-bit unsigned integer value
  • LONG: A 32-bit signed integer value
  • FIXED: A 32-bit fixed point (16.16) value
  • FLOAT: A 32-bit single-precision value
  • DOUBLE: A 64-bit double-precision value
  • QWORD: A 64-bit unsigned integer value
  • LONG64: A 64-bit signed integer value
  • BYTE[n]: “n” bytes
  • STRING:
    • WORD: string length (number of bytes)
    • BYTE[length]: characters (in UTF-8)
    • The '\0' character is not included
  • POINT:
    • LONG: X coordinate value
    • LONG: Y coordinate value
  • SIZE:
    • LONG: Width value
    • LONG: Height value
  • RECT:
    • POINT: Origin coordinates
    • SIZE: Rectangle size
  • PIXEL: One pixel, depending on the image pixel format:
    • RGBA: BYTE[4], each pixel have 4 bytes in this order Red, Green, Blue, Alpha
    • Grayscale: BYTE[2], each pixel have 2 bytes in the order Value, Alpha
    • Indexed: BYTE, each pixel uses 1 byte (the index)
  • TILE: Tilemaps: Each tile can be a 8-bit (BYTE), 16-bit (WORD), or 32-bit (DWORD) value and there are masks related to the meaning of each bit
  • UUID: A Universally Unique Identifier stored as BYTE[16]

Introduction

The format is much like FLI/FLC files, but with different magic number and different chunks. Also, the color depth can be 8, 16 or 32 for Indexed, Grayscale and RGB respectively, and images are compressed with zlib. Color palettes are in FLI color chunks (it could be type=11 or type=4). For color depths more than 8bpp, palettes are optional. To read the sprite:
  1. Read the ASE header
  2. For each frame:
    • Read the frame header
    • For each chunk in this frame:
      • Read the chunk (it should be layer information, a cel or a palette)
A 128-byte header (same as FLC/FLI header, but with other magic number):
DWORD       File size
WORD        Magic number (0xA5E0)
WORD        Frames
WORD        Width in pixels
WORD        Height in pixels
WORD        Color depth (bits per pixel)
              32 bpp = RGBA
              16 bpp = Grayscale
              8 bpp = Indexed
DWORD       Flags:
              1 = Layer opacity has valid value
              2 = Layer blend mode/opacity is valid for groups
              4 = Layers have an UUID
WORD        Speed (milliseconds between frame, like in FLC files)
            DEPRECATED: You should use the frame duration field
DWORD       Set be 0
DWORD       Set be 0
BYTE        Palette entry (index) which represent transparent color
            in all non-background layers (only for Indexed sprites)
BYTE[3]     Ignore these bytes
WORD        Number of colors (0 means 256 for old sprites)
BYTE        Pixel width (pixel ratio is "pixel width/pixel height")
BYTE        Pixel height
SHORT       X position of the grid
SHORT       Y position of the grid
WORD        Grid width (zero if there is no grid)
WORD        Grid height (zero if there is no grid)
BYTE[84]    For future (set to zero)

Frames

After the header come the “frames” data. Each frame has this little header of 16 bytes:
DWORD       Bytes in this frame
WORD        Magic number (always 0xF1FA)
WORD        Old field which specifies the number of "chunks"
            in this frame. If this value is 0xFFFF, we might
            have more chunks to read in this frame
WORD        Frame duration (in milliseconds)
BYTE[2]     For future (set to zero)
DWORD       New field which specifies the number of "chunks"
            in this frame (if this is 0, use the old field)
Then each chunk format is:
DWORD       Chunk size
WORD        Chunk type
BYTE[]      Chunk data
The chunk size includes the DWORD of the size itself, and the WORD of the chunk type, so a chunk size must be equal or greater than 6 bytes at least.

Chunk Types

Old Palette Chunk (0x0004)

Ignore this chunk if you find the new palette chunk (0x2019).
WORD        Number of packets
+ For each packet
  BYTE      Number of palette entries to skip from the last packet
  BYTE      Number of colors in the packet (0 means 256)
  + For each color in the packet
    BYTE    Red (0-255)
    BYTE    Green (0-255)
    BYTE    Blue (0-255)

Layer Chunk (0x2004)

In the first frame should be a set of layer chunks to determine the entire layers layout:
WORD        Flags:
              1 = Visible
              2 = Editable
              4 = Lock movement
              8 = Background
              16 = Prefer linked cels
              32 = The layer group should be displayed collapsed
              64 = The layer is a reference layer
WORD        Layer type
              0 = Normal (image) layer
              1 = Group
              2 = Tilemap
WORD        Layer child level
WORD        Default layer width in pixels (ignored)
WORD        Default layer height in pixels (ignored)
WORD        Blend mode
BYTE        Opacity
BYTE[3]     For future (set to zero)
STRING      Layer name
+ If layer type = 2
  DWORD     Tileset index
+ If file header flags have bit 4:
  UUID      Layer's universally unique identifier

Cel Chunk (0x2005)

This chunk determines where to put a cel in the specified layer/frame.
WORD        Layer index
SHORT       X position
SHORT       Y position
BYTE        Opacity level
WORD        Cel Type
              0 - Raw Image Data
              1 - Linked Cel
              2 - Compressed Image
              3 - Compressed Tilemap
SHORT       Z-Index
BYTE[5]     For future (set to zero)

Tags Chunk (0x2018)

After the tags chunk, you can write one user data chunk for each tag.
WORD        Number of tags
BYTE[8]     For future (set to zero)
+ For each tag
  WORD      From frame
  WORD      To frame
  BYTE      Loop animation direction
              0 = Forward
              1 = Reverse
              2 = Ping-pong
              3 = Ping-pong Reverse
  WORD      Repeat N times
  BYTE[6]   For future (set to zero)
  BYTE[3]   RGB values of the tag color (deprecated)
  BYTE      Extra byte (zero)
  STRING    Tag name

Palette Chunk (0x2019)

DWORD       New palette size (total number of entries)
DWORD       First color index to change
DWORD       Last color index to change
BYTE[8]     For future (set to zero)
+ For each palette entry in [from,to] range
  WORD      Entry flags:
              1 = Has name
  BYTE      Red (0-255)
  BYTE      Green (0-255)
  BYTE      Blue (0-255)
  BYTE      Alpha (0-255)
  + If has name bit in entry flags
    STRING  Color name

User Data Chunk (0x2020)

Specifies the user data (color/text/properties) to be associated with the last read chunk/object.
DWORD       Flags
              1 = Has text
              2 = Has color
              4 = Has properties
+ If flags have bit 1
  STRING    Text
+ If flags have bit 2
  BYTE      Color Red (0-255)
  BYTE      Color Green (0-255)
  BYTE      Color Blue (0-255)
  BYTE      Color Alpha (0-255)
+ If flags have bit 4
  DWORD     Size in bytes of all properties maps
  DWORD     Number of properties maps
  ...

Slice Chunk (0x2022)

DWORD       Number of "slice keys"
DWORD       Flags
              1 = It's a 9-patches slice
              2 = Has pivot information
DWORD       Reserved
STRING      Name
+ For each slice key
  DWORD     Frame number
  LONG      Slice X origin coordinate
  LONG      Slice Y origin coordinate
  DWORD     Slice width
  DWORD     Slice height
  ...

Tileset Chunk (0x2023)

DWORD       Tileset ID
DWORD       Tileset flags
DWORD       Number of tiles
WORD        Tile Width
WORD        Tile Height
SHORT       Base Index
BYTE[14]    Reserved
STRING      Name of the tileset
...

Notes

For complete technical details including all chunk types, compression methods, and implementation notes, please refer to the full specification document in the Aseprite repository.

File Format Changes

The first change from the first release of the new .ase format is the new frame duration field. This is because now each frame can have different duration. How to read both formats (old and new one)? You should set all frames durations to the “speed” field read from the main ASE header. Then, if you found a frame with the frame-duration field > 0, you should update the duration of the frame with that value.

Build docs developers (and LLMs) love