The Three Coordinate Spaces
Every custom entity exists in three overlapping coordinate systems at once. World space is the global grid used by commands, script APIs, and spawn locations. In world space, one unit equals one full block, the Y axis points straight up, +Z is South, and +X is East. World coordinates never rotate when the entity turns.
Entity local space is anchored to the entity's feet (its origin point in the world) and rotates alongside the entity's body or head. Like world space, one unit equals one full block, and +Y is up. When you use caret notation in commands, you are working in entity local space, where the three axes represent left, up, and forward relative to the direction the entity is facing.
Model space (often called bone or pixel space) is used inside resource pack geometry, animations, and render controllers. Here, one unit equals 1/16th of a block—meaning a standard block is 16x16x16 model units. The origin (0, 0, 0) is still at the bottom center of the entity's feet, +Y is still up, and -Z is normally the front of the model. However, the X axis is frequently inverted between your 3D modeling viewport, your geometry JSON, and your keyframe animations.
| Coordinate Space | 1 Unit Equals | Origin Point | Used By |
|---|---|---|---|
| World Space | 1 Block (16 pixels) | World 0, 0, 0 | Absolute/tilde commands, Script API locations, Molang query.position |
| Entity Local Space | 1 Block (16 pixels) | Bottom center of entity feet | Caret (^) commands, minecraft:rideable seat positions, hitbox offsets |
| Model / Bone Space | 1 Pixel (1/16 block) | Bottom center of entity feet (or parent bone pivot) | Geometry cubes/pivots, animation position keyframes |
Converting Between Pixels and Blocks
The most frequent conversion you will perform is translating a visual point on your model into a behavior pack component value. Modeling tools display coordinates in pixels (1/16th of a block), while behavior pack components almost exclusively expect block units.
To convert from Model Space (pixels) to Local Space (blocks), divide your X, Y, and Z values by 16. To convert from Local Space (blocks) to Model Space (pixels), multiply by 16. For example, if you model a saddle on a custom beast and its seat bone pivot is at [0, 24, -4] in your geometry file, the behavior pack needs those numbers divided by 16 to place the rider properly.
Keep in mind that while the Y axis (height) converts cleanly by dividing by 16, the Z axis (forward/backward) in behavior components like seat positions expects positive values to push the rider forward, whereas in geometry files, the front of the entity faces negative Z. You must flip the Z sign when converting a front-facing model coordinate into a seat offset.
{
"format_version": "1.20.40",
"minecraft:entity": {
"description": {
"identifier": "my_pack:custom_mount",
"is_spawnable": true,
"is_summonable": true
},
"components": {
"minecraft:collision_box": {
"width": 1.2,
"height": 1.8
},
"minecraft:rideable": {
"seat_count": 1,
"family_types": ["player"],
"seats": [
{
"position": [0.0, 1.5, 0.25]
}
]
}
}
}
}Axis Inversion in Geometry and Animations
Even within the resource pack, geometry definitions and animation files do not treat axes identically. In a geometry file with format version 1.12.0 or newer, an entity faces North (-Z) by default. From the perspective of someone looking at the front of the entity, the right side of the screen is +X (the entity's left arm), and the left side of the screen is -X (the entity's right arm).
When you write a custom animation in a resource pack, the coordinate system shifts from global model space to parent-relative bone space. A bone's position keyframes are measured as an offset from its rest position defined in the geometry file, not from [0, 0, 0]. Furthermore, Bedrock's animation engine inverts the X and Y rotation signs compared to standard 3D math, and translates +X position keyframes toward the entity's right (-X in geometry space).
If you build animations inside a visual editor, the exporter handles these sign flips behind the scenes. If you are writing procedural animations using Molang inside the JSON file directly, you must invert your X translations manually so that positive X moves a bone toward the entity's right side.
{
"format_version": "1.8.0",
"animations": {
"animation.custom_mount.strafe_lean": {
"loop": true,
"bones": {
"body": {
"position": [
"-math.sin(query.life_time * 100) * 2.0",
0.0,
0.0
],
"rotation": [
0.0,
0.0,
"math.sin(query.life_time * 100) * 10.0"
]
}
}
}
}
}Using Locators to Bridge Model and World Space
Trying to calculate where an entity's hand or jaw is in world space using math is brittle, because animations bend and rotate bones away from their starting offsets. Instead of calculating these offsets manually, use locators inside your geometry file.
A locator is a named point attached to a specific bone in model space. Because it is parented to a bone, it inherits every translation, rotation, and scale applied to that bone and its parents during an animation. You define the locator using pixel coordinates in your geometry file, and the game engine automatically converts its final animated position into world space at runtime.
Once a locator is defined in the geometry, you can reference its name in client_entity particle emitters, sound effects, or animation controllers. The engine spawns the particle at the exact world space location of that pixel coordinate without requiring you to divide by 16 or run trigonometry on the entity's body rotation.
{
"format_version": "1.12.0",
"minecraft:geometry": [
{
"description": {
"identifier": "geometry.custom_mount",
"texture_width": 64,
"texture_height": 64,
"visible_bounds_width": 3,
"visible_bounds_height": 3,
"visible_bounds_offset": [0, 1.5, 0]
},
"bones": [
{
"name": "root",
"pivot": [0, 0, 0]
},
{
"name": "head",
"parent": "root",
"pivot": [0, 20, -8],
"cubes": [
{"origin": [-4, 16, -16], "size": [8, 8, 8], "uv": [0, 0]}
],
"locators": {
"mouth_emitter": [0, 18, -16]
}
}
]
}
]
}Converting Local Offsets to World Coordinates in Commands
When running commands from behavior pack animation controllers or entity events, you often need to affect blocks or summon projectiles in front of the entity. Using tilde (~) notation keeps the offset in world space: `~ ~ ~1` will always target one block South of the entity, regardless of where the entity is looking.
To convert a local offset into a world target inside a command, switch to caret (^) notation: `^left ^up ^forward`. Caret notation automatically applies the entity's rotation matrix to convert local block units into world coordinates.
Be aware that caret notation uses the entity's head rotation (both yaw and pitch) by default. If your entity looks up at the sky, `^ ^ ^3` points into the air three blocks away. If you only want horizontal rotation (yaw) without vertical tilt, you cannot rely on standard caret notation alone; you must either clamp the entity's pitch or calculate the offset in the Script API using the entity's view direction vector.
Trigonometric Conversion in Molang
Occasionally you need to compare a world-space coordinate with an entity's local orientation inside a Molang expression—for instance, checking how far an entity has traveled along its own forward vector, or spawning something at a calculated world offset.
You can get an entity's world coordinates in blocks using `query.position(0)` for X, `query.position(1)` for Y, and `query.position(2)` for Z. To project a local forward distance onto the world X and Z axes using the entity's body yaw (`query.body_y_rotation`), use sine and cosine.
In Bedrock, yaw is 0 when facing South (+Z), 90 when facing West (-X), 180 when facing North (-Z), and -90 (or 270) when facing East (+X). Because of how yaw wraps, a local forward distance `D` converts to a world X offset of `-math.sin(query.body_y_rotation) * D` and a world Z offset of `math.cos(query.body_y_rotation) * D`.
variable.forward_dist = 2.5;
variable.world_target_x = query.position(0) - math.sin(query.body_y_rotation) * variable.forward_dist;
variable.world_target_y = query.position(1) + 1.0;
variable.world_target_z = query.position(2) + math.cos(query.body_y_rotation) * variable.forward_dist;Which versions this applies to
Applies to all modern Bedrock versions using geometry format 1.12.0+ and entity format 1.10.0+. No experimental toggles are required.
When it does not work
| What you see | Why | The fix |
|---|---|---|
| A rider or passenger floats 15 to 20 blocks high in the air above the mount. | You copied pixel coordinates directly from your modeling software into the `position` array of `minecraft:rideable` without converting them to block space. | Divide all three numbers in the seat `position` array by 16, and invert the Z value if the seat is in front of the entity's center. |
| The entity turns invisible whenever the player looks slightly away from its feet, even though the model is huge. | `visible_bounds_width`, `visible_bounds_height`, and `visible_bounds_offset` in the geometry description are written in block space, not pixel space. Setting them too small causes frustum culling to hide the model early. | Measure your model's width and height in pixels, divide by 16, and round up to set the visible bounds in your `.geo.json` file. |
| Content Log shows `[Animation][error]-Bone 'locator_name' not found` or particles spawn at the entity's feet instead of its hands. | The particle effect in the client entity file is trying to attach to a locator, but either the locator name is misspelled or it was placed inside a bone's `cubes` array instead of the `locators` object. | Ensure `locators` is a sibling of `cubes` inside the bone definition in your geometry JSON, and verify the exact string matches the locator name in your animation or particle emitter. |
Reference, straight from the game files
Read out of the game itself, so the ids match the version you are building for.
