Below is a simple and practical structure that should work well for most engines:
{
"texture": "player.png",
"frames": [
{
"name": "idle_down_0",
"rect": { "x": 0, "y": 0, "w": 45, "h": 35 },
"pivot": { "x": 0.5, "y": 0.5 }
},
{
"name": "idle_down_1",
"rect": { "x": 45, "y": 0, "w": 45, "h": 35 },
"pivot": { "x": 0.5, "y": 0.5 }
}
],
"animations": [
{
"name": "idle_down",
"loop": true,
"frames": [
{ "frame": 0, "duration_ms": 1600 },
{ "frame": 1, "duration_ms": 60 }
]
}
],
"pixels_per_unit": 16
}- The filename of the exported PNG sprite sheet
Each frame represents a region of the sprite sheet:
-
name— unique identifier for the frame -
rect— position and size inside the PNG:x,y= top-left cornerw,h= width and height in pixels
-
pivot(optional) — the origin point for the sprite (explained below)
The pivot (or origin) is the point inside the sprite that is used as its position in the game.
For example:
{ "x": 0.5, "y": 0.5 }→ center of the sprite{ "x": 0.5, "y": 1.0 }→ bottom-center (very common for characters standing on the ground){ "x": 0.0, "y": 0.0 }→ top-left corner
The values are normalized:
0.0= start of the frame (left or top)1.0= end of the frame (right or bottom)
If omitted, engines typically assume a default (usually center).
Animations are named sequences of frames:
name— animation nameloop— whether it repeatsframes— ordered list of frames
Each animation frame contains:
frame— index into theframesarrayduration_ms— how long that frame is displayed
pivotcan be omitted per-frame if a default is preferredpixels_per_unitis optional and mainly used for scaling in some engines
- This format intentionally avoids complexity and should be straightforward to generate
- It maps closely to how most engines represent sprite animations internally
- I’m happy to adjust details if something here is difficult to support on your side