How coordinate-based variation works
Texture variation is strictly a client-side rendering feature controlled inside RP/textures/terrain_texture.json. Instead of pointing a texture shortname to a single file path string, you pass an array of objects under the variations key. When the world renderer builds a chunk mesh, it hashes the X, Y, and Z coordinates of each block position and uses that hash to pick one entry from the variations list.
Because the selection is driven by a deterministic coordinate hash rather than a pure random number generator, the chosen texture stays the same every time the chunk loads. All players looking at the same coordinates will see the exact same variant, and breaking and replacing the block at that exact spot will yield the same appearance.
Setting up terrain_texture.json
Open or create RP/textures/terrain_texture.json. Under the texture_data object, define the shortname your custom block will reference. Inside that shortname's object, replace the usual string path with a variations array inside the textures property.
Each item in the variations array is an object containing a path string and an optional weight number. The paths omit the file extension and resolve from the root of the resource pack, just like standard texture definitions.
{
"resource_pack_name": "my_custom_pack",
"texture_name": "atlas.terrain",
"padding": 8,
"num_mip_levels": 4,
"texture_data": {
"wiki:cobbled_shale": {
"textures": {
"variations": [
{
"path": "textures/blocks/cobbled_shale_0",
"weight": 60
},
{
"path": "textures/blocks/cobbled_shale_1",
"weight": 25
},
{
"path": "textures/blocks/cobbled_shale_2",
"weight": 10
},
{
"path": "textures/blocks/cobbled_shale_fossil",
"weight": 5
}
]
}
}
}
}Understanding variation weights
The weight property controls how often a given texture appears relative to the other entries in the same variations list. Weights do not need to add up to 100, though making them sum to 100 makes it easy to read them as percentages. The game totals all weights in the array and gives each entry a slice of the probability pool equal to its weight divided by the total.
If you leave weight off every entry, all variants show up with equal frequency. This is usually what you want for natural noise textures like sand, gravel, or grass tops. Weighted entries are much better for man-made blocks or stone: you can give one clean base texture a weight of 80, two slightly cracked variants a weight of 9 each, and a rare mossy or fossilized variant a weight of 2.
| Field | What it does | Notes |
|---|---|---|
| variations | Holds the list of texture choices for a single shortname. | Must be nested inside the textures object of your shortname entry. |
| path | Points to the PNG or TGA file relative to the resource pack root. | Required on every entry inside variations. Do not include the file extension. |
| weight | Sets the relative chance of this texture being picked. | Optional positive number. If omitted on one entry, set it explicitly on all entries to avoid unpredictable distribution. |
Referencing the shortname on a custom block
Once the shortname is configured in terrain_texture.json, the behavior pack block definition does not need to know that variations exist. You assign the shortname (in this example, wiki:cobbled_shale) to your block the exact same way you assign a single texture.
In modern custom blocks that use minecraft:geometry and minecraft:material_instances, pass the shortname into the texture field of your material instance. If your block uses per-face material instances (such as a pillar with different top and side textures), each shortname can independently use its own variations array in terrain_texture.json.
{
"format_version": "1.20.80",
"minecraft:block": {
"description": {
"identifier": "wiki:cobbled_shale"
},
"components": {
"minecraft:destructible_by_mining": {
"seconds_to_destroy": 1.5
},
"minecraft:geometry": "minecraft:geometry.full_block",
"minecraft:material_instances": {
"*": {
"texture": "wiki:cobbled_shale",
"render_method": "opaque"
}
}
}
}
}Interaction with blocks.json and inventory icons
If you alternatively assign textures to your block using the legacy RP/blocks.json file instead of minecraft:material_instances, the setup works identically: point the textures property in RP/blocks.json to your wiki:cobbled_shale shortname, and the variations defined in terrain_texture.json will render in the world.
Keep in mind that coordinate-based variation only applies to blocks placed in the world. When the block is held in a player's hand, dropped as a floating item, or shown in an inventory slot, there are no world coordinates to hash. In those contexts, Bedrock always renders the first entry (index 0) in your variations array. Always put your cleanest, most representative base texture first in the list so the inventory icon looks right.
Which versions this applies to
Applies to all modern Bedrock versions (1.16.100+ for material_instances, and earlier versions via RP/blocks.json). No experimental toggles are required.
When it does not work
| What you see | Why | The fix |
|---|---|---|
| The block renders with the purple-and-black missing texture checkerboard in the world and in the inventory. | You placed the variations array directly inside the shortname object ("wiki:cobbled_shale": { "variations": [...] }) instead of nesting it inside the "textures" key, or you put an array of objects directly into "textures" without the "variations" wrapper. | Ensure the hierarchy inside texture_data is Shortname -> "textures" -> "variations" -> [ { "path": "..." } ]. |
| Most placed blocks look fine, but random blocks in the world show the purple-and-black checkerboard. | One of the file paths inside your variations array has a typo or the image file is missing from RP/textures/blocks/. The block only breaks at coordinates where the hash selects that specific broken entry. | Temporarily set the weight of suspect entries to a very high number to test them one by one, and verify every path string matches a real PNG file in your resource pack. |
| Content Log shows: [Blocks][error] ... minecraft:material_instances ... texture path not found in terrain_texture.json. | You put the file path (such as textures/blocks/cobbled_shale_0) into the block's material_instances component instead of the shortname key defined under texture_data. | In BP/blocks/, always reference the shortname key ("wiki:cobbled_shale"), never the file path. |
| Visible seams or blurry borders appear between adjacent variants at a distance. | One of your variant PNG files has a different resolution than the others (for example, mixing a 32x32 variant with 16x16 variants), or num_mip_levels in terrain_texture.json is causing color bleed across the texture atlas. | Keep all variant images for a single block at the exact same pixel dimensions and color palette along their outer edges. |
Reference, straight from the game files
Read out of the game itself, so the ids match the version you are building for.
