Format: Sprite Sheet
The add-on exports sprite sheet animations in three formats: PNG (.png), Aseprite JSON (.json), and Packed Binary (.sprsh). All three render animation frames into a grid-based sprite sheet image. The difference is how metadata and the image are delivered.
- PNG (
.png) — A single sprite sheet image with no metadata. - Aseprite JSON (
.json) — Aseprite-compatible JSON metadata alongside a sidecar PNG image. - Packed Binary (
.sprsh) — A single file containing both the Aseprite JSON metadata and the PNG image bytes.
All exportable animations are packed sequentially into a single sheet. Frames are laid out left-to-right, top-to-bottom in the grid.
PNG format (.png)
A single PNG image containing all animation frames arranged in a grid. No metadata is included — the consumer must know the frame size and count ahead of time.
Aseprite JSON format (.json)
An Aseprite-compatible JSON metadata file alongside a sidecar PNG sprite sheet image. This format can be imported by Aseprite and any engine with Aseprite sprite sheet support (Godot, Phaser, LibGDX, etc.).
Schema
{
"frames": [
{
"filename": "idle 0",
"frame": { "x": 0, "y": 0, "w": 256, "h": 256 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 256, "h": 256 },
"sourceSize": { "w": 256, "h": 256 },
"duration": 42
}
],
"meta": {
"app": "Playable Workshop Tools for Blender",
"image": "character.png",
"format": "RGBA8888",
"size": { "w": 1024, "h": 1024 },
"scale": "1",
"frameTags": [
{
"name": "idle",
"from": 0,
"to": 23,
"direction": "forward"
}
]
}
}
Field reference
Frame
| Field | Type | Description |
|---|---|---|
filename | string | Frame identifier ("animation_name index") |
frame | object | Region in the sheet: x, y, w, h (pixels) |
rotated | bool | Always false (no rotation packing) |
trimmed | bool | Always false (no trim packing) |
spriteSourceSize | object | Same as frame (no trimming) |
sourceSize | object | Original frame dimensions: w, h |
duration | int | Frame duration in milliseconds (1000 / fps) |
Meta
| Field | Type | Description |
|---|---|---|
app | string | Exporter identifier |
image | string | Filename of the sidecar PNG image |
format | string | Pixel format (always "RGBA8888") |
size | object | Total sheet dimensions: w, h (pixels) |
scale | string | Scale factor (always "1") |
frameTags | array | Animation tag definitions |
Frame tag
| Field | Type | Description |
|---|---|---|
name | string | Animation name |
from | int | First frame index (inclusive) |
to | int | Last frame index (inclusive) |
direction | string | Playback direction (always "forward") |
Frame indices in
frameTagsare global across all animations. For example, if “idle” uses frames 0–23 and “run” uses frames 24–31, the run tag would have"from": 24, "to": 31.
Packed binary format (.sprsh)
The packed binary format bundles the Aseprite-compatible JSON metadata and the sprite sheet PNG into a single .sprsh file.
Binary layout
Offset Size Type Description
------ ------ ---------- ---------------------------
0 4 char[4] Magic bytes: "SPSH"
4 4 uint32 LE Format version (currently 1)
8 4 uint32 LE JSON data length in bytes (N)
12 4 uint32 LE Image data length in bytes (M)
16 N bytes JSON data (UTF-8, Aseprite schema)
16+N M bytes PNG image bytes
Header (16 bytes)
| Offset | Size | Type | Description |
|---|---|---|---|
| 0 | 4 | char[4] | "SPSH" magic |
| 4 | 4 | uint32 LE | Version (1) |
| 8 | 4 | uint32 LE | JSON length (N) |
| 12 | 4 | uint32 LE | Image length (M) |
Reading a .sprsh file
import struct
import json
with open("character.sprsh", "rb") as f:
magic = f.read(4)
assert magic == b"SPSH"
version, json_len, image_len = struct.unpack("<III", f.read(12))
aseprite_data = json.loads(f.read(json_len).decode("utf-8"))
image_data = f.read(image_len) # Raw PNG bytes
use std::fs;
fn read_sprsh(path: &str) -> (serde_json::Value, Vec<u8>) {
let data = fs::read(path).unwrap();
assert_eq!(&data[0..4], b"SPSH");
let version = u32::from_le_bytes(data[4..8].try_into().unwrap());
let json_len = u32::from_le_bytes(data[8..12].try_into().unwrap()) as usize;
let image_len = u32::from_le_bytes(data[12..16].try_into().unwrap()) as usize;
let aseprite_data: serde_json::Value =
serde_json::from_slice(&data[16..16 + json_len]).unwrap();
let image_data = data[16 + json_len..16 + json_len + image_len].to_vec();
(aseprite_data, image_data)
}
Notes
- The JSON payload inside
.sprshuses the exact same Aseprite-compatible schema as the standalone.jsonformat. - In
.sprshfiles, themeta.imagefield contains just the image name (no file path) since the image is embedded. - Image data is raw PNG file bytes, not decoded pixel data.
- The sidecar PNG (for the
.jsonformat) is saved alongside the JSON file with the same base name.