Wiki 9Minecraft Wiki 9Minecraft

Entity Render Controllers

Bind models, textures, and materials to custom entities dynamically using Molang.

5Sections
4Code examples
3Known errors

A render controller is a resource pack file that tells Minecraft how to draw an entity on screen by linking its geometry, textures, and materials together. While the client entity file defines the raw assets available to a mob, the render controller decides which specific assets are active on any given frame. You reach for a custom render controller whenever your entity needs to change its skin variant, swap models mid-game, hide individual bones, or alter its color tint based on behavior pack data.

File structure and the default controller

Render controllers live in the `RP/render_controllers/` directory of your resource pack. A single JSON file can hold one or more controller definitions inside the `render_controllers` object. Each controller must have a unique identifier, conventionally starting with `controller.render.` followed by your pack namespace or entity name.

At minimum, a render controller needs to output one geometry, at least one material, and at least one texture. These are referenced using the `Geometry.`, `Material.`, and `Texture.` prefixes, which correspond directly to the short-names you defined in your entity's `.entity.json` client file. If your custom entity only ever uses a single model, a single texture, and the standard opaque material, you do not even need to write your own file; you can reference the built-in `controller.render.default` in your client entity definition, provided you name your assets `default`.

RP/render_controllers/goblin.render_controllers.json
{
  "format_version": "1.8.0",
  "render_controllers": {
    "controller.render.my_pack.goblin": {
      "geometry": "Geometry.default",
      "materials": [
        { "*": "Material.default" }
      ],
      "textures": [
        "Texture.default"
      ]
    }
  }
}

Linking to the client entity

A render controller does nothing on its own until it is attached to a client entity definition inside `RP/entity/`. In the `description` block of your client entity file, you define short-names for your textures, geometries, and materials, and then list your controller in the `render_controllers` array.

You can also attach a Molang condition directly to the entry in the `render_controllers` array of the client entity file. This allows you to turn an entire render pass on or off—a technique vanilla Minecraft uses for things like the Creeper's charged armor layer or a saddle on a pig. If you run two render controllers at the same time on one entity, the game actually renders two overlapping meshes.

RP/entity/goblin.entity.json
{
  "format_version": "1.10.0",
  "minecraft:client_entity": {
    "description": {
      "identifier": "my_pack:goblin",
      "materials": {
        "default": "entity_alphatest",
        "glow": "entity_emissive_alpha"
      },
      "textures": {
        "default": "textures/entity/goblin/green",
        "red": "textures/entity/goblin/red",
        "blue": "textures/entity/goblin/blue"
      },
      "geometry": {
        "default": "geometry.goblin",
        "armored": "geometry.goblin_armored"
      },
      "render_controllers": [
        "controller.render.my_pack.goblin"
      ]
    }
  }
}

Dynamic variants using arrays

The real power of a render controller comes from the `arrays` block. Instead of hardcoding a single texture or geometry, you can group multiple short-names into an array and use a Molang expression to pick an index at runtime. Arrays are categorized by type (`textures`, `geometries`, or `materials`) and their names must always begin with `Array.`.

The most common pattern is tying a texture array to `query.variant` or `query.mark_variant`, which read the integer values set by the `minecraft:variant` and `minecraft:mark_variant` components in the behavior pack. If your entity has three skins, you list them in an array and index it with `Array.skins[query.variant]`. The game automatically clamps or wraps out-of-bounds indices in most cases, though keeping your behavior pack variant numbers strictly aligned with your array length is best practice.

RP/render_controllers/goblin_variants.render_controllers.json
{
  "format_version": "1.8.0",
  "render_controllers": {
    "controller.render.my_pack.goblin": {
      "arrays": {
        "textures": {
          "Array.skins": [
            "Texture.default",
            "Texture.red",
            "Texture.blue"
          ]
        },
        "geometries": {
          "Array.models": [
            "Geometry.default",
            "Geometry.armored"
          ]
        }
      },
      "geometry": "Array.models[query.is_sheared]",
      "materials": [
        { "*": "Material.default" }
      ],
      "textures": [
        "Array.skins[query.variant]"
      ]
    }
  }
}

Bone visibility and per-bone materials

Rather than swapping out an entire geometry file just to add a hat, a weapon, or a saddle, it is much cleaner to keep all parts inside one geometry file and toggle them using `part_visibility`. This array takes objects where the key is the bone name (or a wildcard `*` at the end of a string) and the value is a boolean or Molang expression. The rules are evaluated from top to bottom, meaning a later entry will override an earlier one.

The `materials` array works the exact same way. Each object maps a bone name pattern to a `Material.` short-name. Most entities start with `{ "*": "Material.default" }` to apply a base material to every bone in the model, and then follow it with specific overrides if a certain bone needs translucency, culling disabled, or an emissive glow.

RP/render_controllers/goblin_parts.render_controllers.json
{
  "format_version": "1.8.0",
  "render_controllers": {
    "controller.render.my_pack.goblin_parts": {
      "geometry": "Geometry.default",
      "part_visibility": [
        { "*": true },
        { "helmet": "query.is_chested" },
        { "weapon_*": "!query.is_baby" }
      ],
      "materials": [
        { "*": "Material.default" },
        { "eyes": "Material.glow" }
      ],
      "textures": [
        "Texture.default"
      ]
    }
  }
}

Color tinting, lighting, and properties reference

Beyond swapping assets, render controllers offer several properties to manipulate how the final mesh is shaded. These properties accept RGBA channels (each evaluated as a float from `0.0` to `1.0` via Molang) or boolean flags. Note that for `color` or `is_hurt_color` to actually tint your texture, the material applied to the entity must support tinting or overlay mixing in its shader definition.

PropertyWhat it doesNotes
geometrySelects the `Geometry.` short-name or `Array.` lookup to render.Required. Evaluates to a single geometry per controller.
materialsList of bone-to-material mappings evaluated in order.Required. Use `*` as a wildcard for all bones or prefix matches.
texturesList of `Texture.` short-names or `Array.` lookups.Required. Most materials only read the first texture in the list; layered materials read multiple.
part_visibilityList of bone-to-Molang mappings that toggle bone rendering.Optional. Hiding a parent bone in the model also hides all of its child bones.
colorObject with `r`, `g`, `b`, `a` channels (0.0 to 1.0) for base tinting.Optional. Requires a material that supports multiplicative color tinting.
overlay_colorObject with `r`, `g`, `b`, `a` channels for a full-body color flash.Optional. Used by Creepers to flash white before exploding.
is_hurt_colorObject with `r`, `g`, `b`, `a` channels overriding the red damage flash.Optional. Sets the color shown when the entity takes damage.
on_fire_colorObject with `r`, `g`, `b`, `a` channels for tinting when burning.Optional.
light_color_multiplierFloat value that scales ambient lighting on the model.Optional. Useful for darkening or brightening a mob without changing its texture.
ignore_lightingBoolean flag that renders the entity at full brightness in the dark.Optional. Pairs well with ghosts, projectiles, or magic effects.
filter_lightingBoolean flag affecting how surrounding light levels color the entity.Optional.
uv_animObject with `offset` and `scale` two-element arrays `[u, v]`.Optional. Scrolls or scales texture coordinates dynamically via Molang (e.g., Charged Creeper aura).

Which versions this applies to

Format version 1.8.0 (and 1.10.0) is the standard for render controllers and works across all modern Bedrock releases (1.16 through 1.21+). No experimental toggles are required.

When it does not work

What you seeWhyThe fix
The entity renders as a pink-and-black checkerboard texture, or logs `[RenderController][error]-Render controller '...' references a texture '...' that was not found`.The short-name used after `Texture.` in the render controller does not match any key under the `textures` object in your `.entity.json` file, or the `.png` file path in `.entity.json` is misspelled.Check that every `Texture.name` in your render controller has a matching `"name": "textures/entity/..."` entry in the client entity description, and verify the image file exists at that exact path.
The entity is completely invisible in-game, though its shadow is visible and it can still be hit.Either `Geometry.default` points to a geometry identifier that does not exist, or you placed `{ "*": false }` at the bottom of `part_visibility` instead of the top, hiding every bone.Ensure your `part_visibility` array starts with the wildcard `{ "*": true }` or `{ "*": false }` first, followed by specific bone overrides below it, and double-check the geometry identifier in your model file.
Content log shows an error stating an array name is invalid or fails to parse the Molang expression inside `textures` or `geometry`.Array names inside the `arrays` block are required by the engine to start with the exact prefix `Array.` (case-sensitive), and short-names must start with `Texture.`, `Geometry.`, or `Material.`.Rename your custom arrays so they begin with `Array.` (for example, `Array.skins` instead of `skins`).

Reference, straight from the game files

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