How Bedrock Flipbook Animations Work
Unlike entities that can swap textures via render controllers or animate UV coordinates in geometry, blocks use a dedicated texture atlas baking system called a flipbook. During resource pack loading, the game engine reads your vertical sprite sheet, slices it into square frames, and cycles those frames directly inside the terrain texture atlas on every game tick.
Because the animation happens inside the texture atlas itself, every instance of that block in the world animates in complete unison. There is almost zero performance cost for placing thousands of animated blocks, because the game only updates the single atlas slot once per frame rather than updating each block in the world individually.
Preparing the Sprite Sheet
A flipbook texture must be a single PNG file where all animation frames are stacked vertically in a single column. The width of the image determines the size of a single frame, and the total height must be an exact multiple of that width. For example, a standard 16x16 pixel block texture with 4 frames of animation requires an image that is 16 pixels wide and 64 pixels tall.
Frame indices are zero-based, counting from the top of the image down to the bottom. Frame 0 is the top square, Frame 1 is directly beneath it, and so on. If your image height is not an exact multiple of its width, the engine will fail to slice the frames properly and will render the pink-and-black missing texture checkerboard across the block.
Registering the Texture in terrain_texture.json
Before defining the animation parameters, you must assign a short texture name to your sprite sheet in RP/textures/terrain_texture.json. This short name is the link that connects your block definition (in blocks.json or the minecraft:material_instances component) to the flipbook animation file.
Point the texture path directly to your vertical sprite sheet PNG, omitting the file extension just like any static block texture.
{
"resource_pack_name": "custom_blocks_rp",
"texture_name": "atlas.terrain",
"padding": 8,
"num_mip_levels": 4,
"texture_data": {
"my_pack:energy_core": {
"textures": "textures/blocks/energy_core_anim"
}
}
}Defining the Animation in flipbook_textures.json
Create a file named flipbook_textures.json directly inside your RP/textures/ folder. Unlike most Bedrock JSON files that start with a root object and a format version, flipbook_textures.json is simply a top-level JSON array containing one object per animated texture.
In each object, you specify the file path to the sprite sheet, the short name you registered in terrain_texture.json, and how fast the frames should advance. If your pack merges with vanilla and other add-ons, the game automatically combines these arrays in load order.
| Field | What it does | Notes |
|---|---|---|
| flipbook_texture | Path to the vertical sprite sheet PNG relative to the resource pack root. | Required. Do not include the .png extension. |
| atlas_tile | The short texture key defined under texture_data in terrain_texture.json. | Required. Must match the string in terrain_texture.json character for character. |
| atlas_index | Selects a specific sub-texture index if the terrain_texture.json entry is an array of variations. | Optional integer. Defaults to 0. |
| ticks_per_frame | How many game ticks (1/20th of a second) each frame stays on screen before advancing. | Optional integer. Lower numbers play faster. 1 is 20 FPS. |
| frames | An array of zero-based integers defining the playback order of the frames. | Optional. If omitted, plays from top to bottom in sequential order. Repeat numbers to hold a frame longer. |
| blend_frames | Smoothly cross-fades the opacity between the current frame and the next frame. | Optional boolean. Defaults to false. Ideal for slow animations like lava, glowing cores, or pulsing lights. |
[
{
"flipbook_texture": "textures/blocks/energy_core_anim",
"atlas_tile": "my_pack:energy_core",
"ticks_per_frame": 4,
"frames": [
0,
1,
2,
3,
2,
1
],
"blend_frames": true
}
]Applying the Animated Texture to a Custom Block
Once your short name is registered in terrain_texture.json and linked in flipbook_textures.json, you apply it to your block the exact same way you apply a static texture. In modern custom blocks, this means referencing the short name inside the minecraft:material_instances component in your Behavior Pack block file (or inside RP/blocks.json if you are using legacy resource definitions).
If your custom block uses a custom geometry via minecraft:geometry, make sure the UV mapping on your model expects a standard single square texture (such as 16x16), not the full height of the vertical strip. The engine handles slicing the strip into a square before applying it to your geometry UVs.
{
"format_version": "1.20.80",
"minecraft:block": {
"description": {
"identifier": "my_pack:energy_core"
},
"components": {
"minecraft:destructible_by_mining": {
"seconds_to_destroy": 2.0
},
"minecraft:geometry": "minecraft:geometry.full_block",
"minecraft:material_instances": {
"*": {
"texture": "my_pack:energy_core",
"render_method": "opaque"
}
}
}
}
}Which versions this applies to
Applies to all modern Bedrock versions (1.16.100+ for material_instances, 1.2+ for flipbook_textures.json). Stable; no experimental toggles are required.
When it does not work
| What you see | Why | The fix |
|---|---|---|
| The block renders in the world, but only shows the very top frame and never animates. | The game found the PNG via terrain_texture.json, but did not match it to an entry in flipbook_textures.json. Either atlas_tile does not exactly match the key in terrain_texture.json, or flipbook_textures.json is in the wrong folder. | Ensure flipbook_textures.json is located directly in RP/textures/ and that atlas_tile matches your terrain_texture.json key exactly, including any namespace prefix. |
| The block renders with the pink and black missing texture checkerboard, or Content Log reports an invalid flipbook frame size. | The height of the PNG file is not an exact multiple of its width, or the frames array references a frame index that does not exist on the sprite sheet (for example, requesting frame 4 on a 4-frame sheet where indices are 0, 1, 2, and 3). | Check the pixel dimensions of your PNG so height divided by width equals a whole number, and verify that the highest number in your frames array is at least 1 less than your total frame count. |
| The block texture looks squished vertically, packing every frame onto the block face at the same time. | Your custom block uses a Blockbench model where the UV box height was stretched to cover the entire vertical sprite sheet, or flipbook_textures.json failed to parse due to a JSON syntax error so the engine treated the strip as a normal static texture. | Validate flipbook_textures.json for trailing commas or missing brackets. If using custom geometry, set the texture resolution in Blockbench to a square (such as 16x16) and map the UVs to that square only. |
Reference, straight from the game files
Read out of the game itself, so the ids match the version you are building for.
