Wiki 9Minecraft Wiki 9Minecraft

Aseprite Animations in JSON UI

Use exported Aseprite sprite sheets and frame metadata to drive UV flipbook animations in Bedrock UI.

5Sections
3Code examples
3Known errors

Minecraft Bedrock's JSON UI engine natively reads the JSON data format exported by the pixel art tool Aseprite. Instead of manually calculating pixel coordinates and writing dozens of chained keyframe blocks by hand, you can export a sprite sheet alongside its metadata file and let the UI engine play the frames automatically. Reach for this workflow whenever you are adding complex frame-by-frame pixel art animations—such as custom loading spinners, animated HUD icons, or dialogue portraits—to your interface.

How Bedrock Reads Aseprite Data

Every image control in JSON UI has a base texture, a UV starting coordinate, and a UV size. Normally, animating a sprite sheet requires defining a `uv` animation type and chaining individual keyframes, where each keyframe shifts the UV coordinate to the next cell in the grid and waits for a set duration.

However, the UI parser includes built-in support for Aseprite's JSON Array export structure. When you place the exported `.json` file in the exact same folder as your `.png` sprite sheet and give them identical base filenames, the game automatically links them. You can then bypass standard keyframe definitions in your UI files and instead reference the frame tags or frame sequences generated by Aseprite.

Exporting from Aseprite

To generate a compatible sprite sheet in Aseprite, open your animated sprite and go to File > Export Sprite Sheet (or press Ctrl+E). In the Layout tab, any sheet type (Horizontal Strip, Vertical Strip, or Packed) will work because the engine reads the exact coordinates of each cell from the JSON file rather than assuming a uniform grid. However, avoid enabling trimming or extruded borders until you have tested your base setup, as trimmed frames can shift the visual anchor of the image inside its UI control.

Switch to the Output tab. Check both Output File (saving your PNG inside your resource pack's `textures/ui/` folder) and JSON Data. Both files must share the exact same name, such as `loading_spinner.png` and `loading_spinner.json`. Directly beneath the JSON Data path, change the JSON format dropdown from Hash to Array. This step is critical: Bedrock's UI parser iterates through the `frames` property as an indexed JSON array `[...]` and will fail to read the default Hash object `{...}`.

If your file contains multiple animations—for example, an icon that has both an 'idle' loop and an 'active' burst—open the Meta section in the Output tab and ensure Tags is checked. This writes your timeline tags into the `meta.frameTags` array, allowing your UI controls to play specific frame ranges by name.

RP/textures/ui/loading_spinner.json
{
  "frames": [
    {
      "filename": "loading_spinner 0.aseprite",
      "frame": { "x": 0, "y": 0, "w": 16, "h": 16 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 16, "h": 16 },
      "sourceSize": { "w": 16, "h": 16 },
      "duration": 100
    },
    {
      "filename": "loading_spinner 1.aseprite",
      "frame": { "x": 16, "y": 0, "w": 16, "h": 16 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 16, "h": 16 },
      "sourceSize": { "w": 16, "h": 16 },
      "duration": 100
    }
  ],
  "meta": {
    "app": "http://www.aseprite.org/",
    "version": "1.2.40-x64",
    "image": "loading_spinner.png",
    "format": "RGBA8888",
    "size": { "w": 32, "h": 16 },
    "scale": "1",
    "frameTags": [
      { "name": "spin", "from": 0, "to": 1, "direction": "forward" }
    ]
  }
}

Understanding the Exported Metadata

Before wiring up the UI control, it helps to understand which parts of the exported JSON file the game actually consumes. If you don't own Aseprite, you can even write or generate this file yourself using any sprite sheet packer that supports the standard JSON-Array texture atlas format.

Note that Aseprite records frame durations in milliseconds (where 100 equals a tenth of a second), whereas standard JSON UI animations define their `duration` in seconds. When Bedrock parses an Aseprite JSON file, it converts the millisecond values for you automatically.

FieldWhat it doesNotes
framesHolds the ordered list of animation cells.Must be a JSON Array `[...]`. If exported as a Hash `{...}`, the animation will not play.
frames[].frameDefines the `x`, `y`, `w`, and `h` pixel bounds on the sheet.Used by the UI engine to set the `uv` and `uv_size` dynamically for each frame.
frames[].durationHow long the frame stays on screen, in milliseconds.Allows variable timing per frame without writing chained UI animation blocks.
meta.frameTagsDefines named sub-animations with `from` and `to` indices.Optional. Lets you store multiple animations on one texture sheet.

Setting Up the Image Control in JSON UI

Once both the `.png` and `.json` files are sitting in your `textures/ui/` folder, you can create your image control in your UI file. Just like any other animated UV texture, the image control needs its `texture` path pointing to the sprite sheet (without the file extension) and an explicit `uv_size` matching a single frame's dimensions.

For standard keyframe animations, you attach an animation definitions array to the `uv` property using the `@namespace.anim_name` syntax. When using an Aseprite sheet, you still attach a `uv` animation, or configure the image control's `aseprite_flip_book` properties depending on how your animation is structured. Below is the foundational setup of the image control alongside a standard UV animation wrapper that initializes the flipbook coordinates.

RP/ui/hud_screen.json
{
  "namespace": "custom_hud",

  "spinner_uv_anim": {
    "anim_type": "aseprite_flip_book",
    "initial_uv": [ 0, 0 ]
  },

  "custom_spinner_image": {
    "type": "image",
    "texture": "textures/ui/loading_spinner",
    "size": [ 16, 16 ],
    "uv_size": [ 16, 16 ],
    "uv": "@custom_hud.spinner_uv_anim"
  }
}

When to Use Manual UV Animations Instead

While `aseprite_flip_book` is convenient for complex pixel art with variable frame timings, it requires an extra JSON file in your textures directory for every animated asset. For simple two- or three-frame animations with constant frame durations, you can skip the Aseprite JSON export entirely and use a standard `flip_book` animation type right inside your UI file.

A native `flip_book` animation only requires the PNG strip. You specify `frame_count`, `frame_step` (how many pixels to advance the UV on each frame), and `fps`. Reserve the Aseprite JSON workflow for animations where individual frames hold for different lengths of time (such as a glint that pauses for two seconds before flashing across an icon) or when your artist is already authoring assets directly in Aseprite.

RP/ui/simple_flipbook_example.json
{
  "namespace": "simple_anim",

  "basic_strip_anim": {
    "anim_type": "flip_book",
    "initial_uv": [ 0, 0 ],
    "frame_count": 4,
    "frame_step": 16,
    "fps": 10,
    "reversible": false
  },

  "animated_icon": {
    "type": "image",
    "texture": "textures/ui/four_frame_strip",
    "size": [ 16, 16 ],
    "uv_size": [ 16, 16 ],
    "uv": "@simple_anim.basic_strip_anim"
  }
}

Which versions this applies to

Applies to Minecraft Bedrock 1.16.0 and later, including current 1.21.x releases. JSON UI and Aseprite flipbook animations are part of the core resource pack system and do not require any experimental toggles.

When it does not work

What you seeWhyThe fix
The entire sprite sheet is squished into the UI control at once, or stays frozen on the first frame.During export from Aseprite, the JSON Data format was left on the default 'Hash' setting instead of 'Array', or the `.json` file does not have the exact same filename and folder path as the `.png` file.Re-export the sprite sheet from Aseprite with the JSON format dropdown set to 'Array', and verify that both the `.png` and `.json` sit side-by-side in the same `textures/` subdirectory.
The animation plays, but frames jitter, jump around, or stretch to fill the image control.'Trim Sprite' or 'Trim Cels' was checked in Aseprite's export dialog, causing each frame in the JSON metadata to have different `w` and `h` values.Uncheck all Trim options in Aseprite when exporting for Bedrock UI so that every frame retains the full canvas bounds matching your control's `uv_size`.
Content Log shows a UI JSON parse error or missing texture warning (pink and black checkerboard).You included the `.png` or `.json` file extension inside the `texture` property of the image control, or placed the Aseprite JSON file inside the `RP/ui/` folder instead of next to the texture.Keep both the image and its Aseprite metadata file inside `RP/textures/...` and reference the path without an extension (for example, `"textures/ui/loading_spinner"`).

Reference, straight from the game files

Read out of the game itself, so the ids match the version you are building for.