Dynamic textures
Swap a model part's texture or geometry by what the held item holds: the item a sulfur cube has absorbed, or a bucketed axolotl's variant.
-
Punchy! Studio
This page has not been translated into Dutch yet, so it is shown in English. Read in English
On this page
A dynamic texture is a rule that changes how a model part looks by what the held item carries. Punchy! reads two things for this: the block a sulfur cube has absorbed, and the variant of a bucketed axolotl. This page is for pack makers who already have a model part definition and want a bucket that shows what is inside it.
Before you start
- Install Punchy! 3.0 or newer, from Modrinth or CurseForge. The keys on this page were checked against Punchy! 3.0 for Fabric on Minecraft 26.3.
- The sulfur cube source is read in the Punchy! builds for Minecraft 26.2, 26.3 and 26.4, the versions that have the sulfur cube. The axolotl source needs no sulfur cube and works on older versions too, for example 1.21.11.
- You need a resource pack with a working definition for a bucket. Model part definitions shows how to make one, and Geometry and textures how to build the model.
- The model needs a bone for the part that changes, such as the contents of the bucket. The texture swap works on one bone at a time.
- For the check in Punchy! Studio you need Punchy! Studio, from Modrinth or CurseForge.
What the two sources read
Every rule names a source. The source decides which held item it reads and what it gets from it.
source | The definition covers | What the rule gets | Examples of what you write in map |
|---|---|---|---|
sulfur_ | minecraft: | The item id of the block the sulfur cube had absorbed when you picked it up. | minecraft:, minecraft: |
axol_ | minecraft: | The variant of the axolotl in the bucket, as the game names it. | lucy, wild, gold, cyan, blue |
Punchy! reads the stack in your hand every frame, so the model changes the moment you swap buckets. Each hand reads its own stack.
When the held item has nothing to report, for example a sulfur cube bucket whose cube absorbed nothing, the rule gets no key. Then only the fallback applies. A rule without a fallback leaves the bone on its base texture when no key matches, so add a fallback when you want a fixed look.
A rule can change one of two things:
- A texture rule gives one bone another texture. The shape stays. This is the default.
- A geo rule swaps the whole geometry for another file. Use it only when the shape itself changes.
Make a bucket that shows its contents
You will make a small sulfur cube bucket model. The body keeps its texture. A flat contents layer on top changes by the block the cube absorbed: red for TNT, brown for oak logs, white for everything else.
-
In your game’s
resourcepacksfolder, make a folder calledDynamic textures. Inside it, makepack.mcmetawith the text below. The numbers are for Minecraft 26.3. Resource packs has the other versions.{ "pack": { "description": "Dynamic textures: sulfur and axolotl buckets", "pack_format": 97, "min_format": 97, "max_format": 97 } } -
Next to
pack.mcmeta, create the folderassets/minecraft/punchy/model_parts_items/definitions. Besidedefinitions, create the foldersgeoandtextures. -
Save the geometry below as
geo/sulfur_bucket.geo.json.It has two bones.
bucketis the body andcontentsis a child bone on top. The rule will retexturecontentsonly.{ "format_version": "1.12.0", "minecraft:geometry": [ { "description": { "identifier": "geometry.sulfur_bucket", "texture_width": 16, "texture_height": 16 }, "bones": [ { "name": "bucket", "pivot": [0, 0, 0], "cubes": [ { "origin": [-3, 0, -3], "size": [6, 5, 6], "uv": { "north": { "uv": [0, 0], "uv_size": [16, 16] }, "south": { "uv": [0, 0], "uv_size": [16, 16] }, "east": { "uv": [0, 0], "uv_size": [16, 16] }, "west": { "uv": [0, 0], "uv_size": [16, 16] }, "up": { "uv": [0, 0], "uv_size": [16, 16] }, "down": { "uv": [0, 0], "uv_size": [16, 16] } } } ] }, { "name": "contents", "parent": "bucket", "pivot": [0, 5, 0], "cubes": [ { "origin": [-2, 5, -2], "size": [4, 2, 4], "uv": { "north": { "uv": [0, 0], "uv_size": [16, 16] }, "south": { "uv": [0, 0], "uv_size": [16, 16] }, "east": { "uv": [0, 0], "uv_size": [16, 16] }, "west": { "uv": [0, 0], "uv_size": [16, 16] }, "up": { "uv": [0, 0], "uv_size": [16, 16] }, "down": { "uv": [0, 0], "uv_size": [16, 16] } } } ] } ] } ] } -
In any paint program, make four PNG files, each 16 by 16 pixels and filled with one flat color, and put them in
textures/. Flat colors are enough to see the result.sulfur_bucket.png, a grey square, for the body.sulfur_tnt.png, a red square.sulfur_oak_log.png, a brown square.sulfur_other.png, a white square.
-
Save the definition below as
definitions/sulfur_bucket.json.{ "items": ["minecraft:sulfur_cube_bucket"], "geo": "minecraft:punchy/model_parts_items/geo/sulfur_bucket.geo.json", "texture": "minecraft:punchy/model_parts_items/textures/sulfur_bucket", "dynamic_bone_textures": [ { "type": "texture", "bone": "contents", "source": "sulfur_cube_content", "fallback": "minecraft:punchy/model_parts_items/textures/sulfur_other", "map": { "minecraft:tnt": "minecraft:punchy/model_parts_items/textures/sulfur_tnt", "minecraft:oak_log": "minecraft:punchy/model_parts_items/textures/sulfur_oak_log" } } ] } -
Turn the pack on under Options, then Resource Packs…, and press Done. If a world is open, press F3+T to reload the resources.
-
Get a sulfur cube bucket whose cube had absorbed a block: pick the cube up with a bucket. Hold the bucket and switch to first person.
-
Pick up another cube that holds a different block and compare. The top layer takes the color of its block. A cube that holds a block the map does not list, and a bucket with nothing, show the white fallback.
Video coming soon
To check the fallback without catching a cube, run /give @s sulfur_cube_bucket. It has no content, so the top layer is white.
What each part of the rule does:
typeistexture, so one bone changes. It is also the default.bonenames the bone to retexture. Without it, a texture rule is thrown away.sourcesays to read the sulfur cube content. It is also the default when you leavesourceout.mappairs a block item id with the texture to use. Write the id with its namespace, asminecraft:tnt.fallbackis the texture for every other case.
The same rule for an axolotl bucket
An axolotl bucket keeps the variant of the axolotl in it. Use axol_variants as the source and write each map key as the variant’s name exactly as the game spells it: lucy, wild, gold, cyan and blue. Punchy! passes the name through unchanged, so the spelling has to match.
This example has one bone, axolotl, and a texture for each variant.
{
"format_version": "1.12.0",
"minecraft:geometry": [
{
"description": { "identifier": "geometry.axolotl_bucket", "texture_width": 16, "texture_height": 16 },
"bones": [
{
"name": "axolotl",
"pivot": [0, 0, 0],
"cubes": [
{
"origin": [-3, 0, -5],
"size": [6, 3, 10],
"uv": {
"north": { "uv": [0, 0], "uv_size": [16, 16] },
"south": { "uv": [0, 0], "uv_size": [16, 16] },
"east": { "uv": [0, 0], "uv_size": [16, 16] },
"west": { "uv": [0, 0], "uv_size": [16, 16] },
"up": { "uv": [0, 0], "uv_size": [16, 16] },
"down": { "uv": [0, 0], "uv_size": [16, 16] }
}
}
]
}
]
}
]
}
Save it as geo/axolotl_bucket.geo.json. In textures/, make five PNG files, each 16 by 16 pixels and filled with one flat color, so the variants are easy to tell apart:
axolotl_lucy.png, pink.axolotl_wild.png, brown.axolotl_gold.png, yellow.axolotl_cyan.png, light blue green.axolotl_blue.png, blue.
Then save this as definitions/axolotl_bucket.json.
{
"items": ["minecraft:axolotl_bucket"],
"geo": "minecraft:punchy/model_parts_items/geo/axolotl_bucket.geo.json",
"texture": "minecraft:punchy/model_parts_items/textures/axolotl_lucy",
"dynamic_bone_textures": {
"type": "texture",
"bone": "axolotl",
"source": "axol_variants",
"fallback": "minecraft:punchy/model_parts_items/textures/axolotl_lucy",
"map": {
"lucy": "minecraft:punchy/model_parts_items/textures/axolotl_lucy",
"wild": "minecraft:punchy/model_parts_items/textures/axolotl_wild",
"gold": "minecraft:punchy/model_parts_items/textures/axolotl_gold",
"cyan": "minecraft:punchy/model_parts_items/textures/axolotl_cyan",
"blue": "minecraft:punchy/model_parts_items/textures/axolotl_blue"
}
}
}
Here dynamic_bone_textures is one object, not a list. Both forms work. To get a bucket of a given variant, run /give @s axolotl_bucket[axolotl/variant="blue"], or catch an axolotl with a bucket.
Video coming soon
Swap the whole geometry
Use a geo rule when the shape changes: a bucket that is full of TNT looks different from an empty one, not only recolored. A geo rule needs no bone.
Make two more geometry files next to geo/sulfur_bucket.geo.json. Keep the same bone names in all of them, because the texture, glow and texture animation rules find a bone by its name.
- Copy
geo/sulfur_bucket.geo.jsontogeo/sulfur_bucket_plain.geo.json. Change itsidentifiertogeometry.sulfur_bucket_plainand leave the rest as it is. - Copy it again as
geo/sulfur_bucket_tnt.geo.json. Change itsidentifiertogeometry.sulfur_bucket_tnt. In thecontentsbone, change"size": [4, 2, 4]to"size": [4, 4, 4], so the top layer is twice as tall.
Then write the rule. Save it as definitions/sulfur_bucket.json in place of the first definition, or copy its rule into the dynamic_bone_textures list of that file, after the texture rule. If you replaced the file, the whole model uses textures/sulfur_bucket.png, because no texture rule is left. Hold a sulfur cube bucket whose cube absorbed TNT and the top layer is taller; with any other content, or none, you see the plain shape.
{
"items": ["minecraft:sulfur_cube_bucket"],
"geo": "minecraft:punchy/model_parts_items/geo/sulfur_bucket.geo.json",
"texture": "minecraft:punchy/model_parts_items/textures/sulfur_bucket",
"dynamic_bone_textures": [
{
"type": "geo",
"source": "sulfur_cube_content",
"fallback": "minecraft:punchy/model_parts_items/geo/sulfur_bucket_plain",
"map": {
"minecraft:tnt": "minecraft:punchy/model_parts_items/geo/sulfur_bucket_tnt"
}
}
]
}
The top level geo is still needed. It is the model Punchy! draws when no geo rule picks one. With a fallback, a geo rule always picks one, so the top level geo only has to exist.
A geo rule changes the shape only. The main texture and your other bone rules stay, so you can combine a geo rule with texture rules in the same list. The geo ids are written differently from the top level geo:
| You write | Punchy! loads |
|---|---|
minecraft: | assets/ |
minecraft: | the same file |
minecraft: | the same file, with .json turned into .geo.json |
minecraft: | assets/, because geo/ is put in front |
Write the full .geo.json path in the top level geo. Only the geo rules add the ending for you.
The files you end up with
Here is the whole pack from this page. The two sulfur_bucket_plain and sulfur_bucket_tnt files are for the geo rule only.
Dynamic textures/
pack.mcmeta
assets/minecraft/punchy/model_parts_items/
definitions/
sulfur_bucket.json
axolotl_bucket.json
geo/
sulfur_bucket.geo.json
sulfur_bucket_plain.geo.json
sulfur_bucket_tnt.geo.json
axolotl_bucket.geo.json
textures/
sulfur_bucket.png
sulfur_tnt.png
sulfur_oak_log.png
sulfur_other.png
axolotl_lucy.png
axolotl_wild.png
axolotl_gold.png
axolotl_cyan.png
axolotl_blue.png
How to write the texture ids
The texture ids in fallback and map follow the same rule as the top level texture.
| You write | Punchy! loads |
|---|---|
minecraft: | assets/ |
mypack: | assets/ |
minecraft: | assets/, a texture the game already has |
The ending .png is added when it is missing. A path that does not start with punchy/model_parts_items/textures/ gets textures/ put in front, and a path that already starts with textures/ is kept as it is. Ids use lower case letters, digits, _, -, . and /. An id with a capital letter or a space is not valid, and Punchy! skips that entry.
Give every texture in one rule the same layout as the main texture. The faces of the geometry read the same UV rectangles whichever PNG is on the bone.
How Punchy! applies the rules
- The rules are read in the order you wrote them. A texture rule for a bone that already has a texture from
bone_texturesreplaces it. Two texture rules for the same bone: the later one wins, as long as it resolves to a texture. - For one rule, Punchy! looks up the key in
map. The match is exact and case sensitive, sobluematchesblueand notBlue. Spaces around a key inmapare trimmed. Whenmaphas no entry, thefallbackis used. When there is nofallbackeither, the bone keeps the texture it had. - Child bones use their parent’s texture unless they have a texture of their own. A rule on
bucketin the example above would retexturecontentstoo, untilcontentshas a rule or abone_texturesentry. - The bone name is found by its exact spelling first, then without regard to case.
- Geo rules: the last rule that picks a geometry wins. A rule picks one when its map has the key, or when it has a
fallback. - The swapped texture is the one the bone’s glow and texture animation use. A bone with
bone_emissionglows with the new texture. A bone withbone_texture_animneeds every texture in its map to be exactlyframestimes the frame height tall (frame_height, or the geometry’s texture height when it is not set), or that texture stays still. - Glow and per-bone texture animation match the bone’s own name only. A child bone that takes its texture from a parent does not glow from the parent’s
bone_emission. The whole-modeltexture_animalso applies to a swapped texture unless the bone has its ownbone_texture_animentry, so the same height rule applies. - The
typeandsourcewords are not case sensitive. Anytypeother thangeocounts astexture.
Check it in Punchy! Studio
Punchy! Studio does not edit dynamic rules. It keeps them when you import a model part and export it again, shows each one in a list, and its live link shows what the real bucket in your hand picks.
- Put your pack on, join a world, hold your bucket and switch to first person. Press F6 to open the editor. If it opens in the Third Person perspective, pick First Person with the switch between the menus and the workspace tabs. Then choose the Model workspace.
- Choose File > Open Model Part… (Ctrl+O). In the Model Parts Library, pick Resource Packs, select your definition and press Open.
- Open the Material tab. Under Dynamic Textures each rule is listed as its type, its bone and its source, for example
texture contents sulfur_cube_content. - Choose View > Live Link with Punchy (Ctrl+L). Punchy! now draws the model in your hand through its own loader, so swapping the bucket in your hand swaps the texture.
- Choose File > Export Model Part… (Ctrl+E), type the name of the pack in Pack and press Export. The rules are written back with the model part.
The stage on the left does not hold a bucket. It shows the fallback of every texture rule and does not swap geometry. Trust the live link for what the bucket in your hand will look like.
Video coming soon
Keys of a dynamic rule
dynamic_bone_textures sits at the root of the definition. It is one rule object or a list of them. The alias dynamicBoneTextures is read only when dynamic_bone_textures is missing.
| Key | Type | Default | What it does | Example |
|---|---|---|---|---|
type | string | texture | texture gives one bone another texture. geo swaps the whole geometry. Case does not matter, and any other value counts as texture. | geo |
bone | string | The bone to retexture. Required for texture: a texture rule without it is thrown away. Ignored for geo. | contents | |
source | string | sulfur_ | What the rule reads. sulfur_ reads the block a sulfur cube absorbed. axol_ reads the variant of a bucketed axolotl. Case does not matter. A missing or blank value means sulfur_. Any other value matches nothing, so only the fallback applies. | axol_ |
map | object | empty | Maps what the source reports to a texture id, or a geometry id for geo. Keys match exactly, with case. A map that is not an object counts as empty. Entries with a value that is not valid text or not a valid id are skipped, and the rest of the rule still works. | { "minecraft: |
fallback | resource id | Used when the map has no entry for the key, and when the held item reports nothing. A texture id for texture, a geometry id for geo. | minecraft: |
| Key | Type | What it does | Example |
|---|---|---|---|
sulfur_ | item id | The registry id of the block item the cube had absorbed, written with its namespace. A bucket whose cube absorbed nothing reports nothing. | minecraft: |
axol_ | text | The variant name of the axolotl, as the game spells it: lucy, wild, gold, cyan or blue. Punchy! passes the name through unchanged, so match the spelling exactly. | blue |
Things that drop a rule
A rule that is not usable is thrown away without a message. The other rules of the definition still work, and so does the rest of the definition.
| What is wrong | Example |
|---|---|
A texture rule with no bone. | "type": "texture" and no "bone". |
A rule with no usable fallback and no usable map entry. | A fallback with a capital letter and no map entries. A map that is not an object and no fallback. |
A map entry whose id is not valid. | "minecraft:. Only that entry is skipped, and the rest of the rule still works. |
dynamic_ that is neither an object nor a list of objects. | "dynamic_ |
A bone that is not text on a texture rule. | "bone": ["contents"] |
Why my bucket does not change
- The mapped texture never shows. The
sourceis misspelled, for exampleaxolotl_variants. Any value other than the two names matches nothing, so only thefallbackshows. - Only the fallback shows. The map key does not match. For sulfur cubes, write the full id:
minecraft:tnt, nottnt. For axolotls, write lower case:blue, notBlue. Keys match exactly. - A bucket from
/giveshows the fallback. A bucket with no content or no variant reports nothing. Catch a cube or an axolotl with a bucket, or give the variant with/give @s axolotl_bucket[axolotl/variant="gold"]. - The bone keeps its old texture. The
bonename is not in the geometry, so check its spelling against the geometry. Or a later texture rule for the same bone replaces yours, because the later rule wins when it resolves to a texture. Check the order of the rules in the list. - The bone does not show the texture you expect. The file is not at the id you wrote, or the id points at another namespace. Punchy! reads the size of each texture when it loads the definition and says nothing when a file is missing.
- Punchy! draws nothing for the bucket. A geometry in a geo rule is missing or broken. Punchy! then leaves the model out and the vanilla bucket shows. Check every geo id in
mapandfallback. - A texture animation stands still. The swapped PNG is not exactly
framestimes the frame height tall. - Nothing happened after an edit. Press F3+T to reload the resources.
- The model is not drawn while you use the bucket. While you use an item, Punchy! lets the vanilla model draw it, unless the definition has an entry of a use or consume type or a
usingstate model. See Model part definitions for the entry types. - Another definition covers the bucket. Among definitions with no
customName, the one whose id sorts first wins. A definition with acustomNamewins over those when the held stack carries that name. See Which definition covers an item.
Related pages
- Model parts overview for the whole system.
- Model part definitions for the file these rules sit in.
- Geometry and textures to build the bones and textures a rule swaps.
- Item clips to animate the bucket’s bones.
- Conditions to play a clip by water, weather or biome.
- Variants and presets to ship several versions of one model.
- Item physics for the axolotl style feedback pose.
- Tutorial: a bucket pack for a whole pack built with these rules.
Last checked against the mod on 29 October 2026.