Wiki 9Minecraft Wiki 9Minecraft

Effects in Animations: Sound and Particle Keyframes

How to trigger sounds and particles at exact timestamps in Bedrock animations.

5Sections
4Code examples
4Known errors

In Minecraft Bedrock Edition, animations do more than just rotate and translate model bones; they can also trigger visual and audio effects at exact timestamps. By using the sound_effects and particle_effects blocks inside a resource pack animation, you can synchronize footsteps, attack swooshes, and spell particles directly to your entity's movement. This approach is much more reliable than trying to time sounds from behavior pack components or scripts, because the client handles the timing alongside the visual keyframes.

The Indirection System (Short Names)

Before writing effect keyframes in an animation file, you must understand how Bedrock resolves effect names. Animations do not use full sound event names (like mob.zombie.say) or full particle identifiers (like minecraft:heart_particle) directly in the keyframe timeline. Instead, they reference a short name—an alias defined in the entity's client definition file (.entity.json).

This indirection allows you to reuse a single animation across multiple entities while swapping out the actual sounds or particles each entity plays. For example, a generic walk animation can trigger a short name called step, and each entity using that animation can map step to a different sound event in its own client entity file.

If an animation triggers a short name that is not listed in the entity's description, the game will silently fail to play the effect or throw a content log warning, even if the short name happens to match a real sound or particle identifier.

RP/entity/golem_guard.entity.json
{
  "format_version": "1.10.0",
  "minecraft:client_entity": {
    "description": {
      "identifier": "custom:golem_guard",
      "materials": {
        "default": "entity_alphatest"
      },
      "textures": {
        "default": "textures/entity/golem_guard"
      },
      "geometry": {
        "default": "geometry.golem_guard"
      },
      "animations": {
        "smash": "animation.golem_guard.smash"
      },
      "sound_effects": {
        "woosh": "mob.irongolem.throw",
        "impact": "mob.irongolem.hit"
      },
      "particle_effects": {
        "dust": "minecraft:cauldron_explosion_emitter",
        "sparks": "custom:anvil_sparks"
      },
      "scripts": {
        "animate": [
          "smash"
        ]
      },
      "render_controllers": [
        "controller.render.default"
      ]
    }
  }
}

Adding Sound Effects to an Animation

Once your short names are mapped in the client entity file, you can schedule them inside your animation file under the sound_effects object. This object sits at the same level as loop, animation_length, and bones inside a specific animation definition.

The keys inside sound_effects are timestamps in seconds, written as strings (such as "0.0" or "0.45"). At each timestamp, you can provide either a single effect object or an array of effect objects if you need multiple sounds to fire at the exact same frame.

Each sound effect object requires the effect property, whose value is the short name from your client entity file. The sound will automatically play at the entity's current world position in 3D space.

RP/animations/golem_guard.animation.json
{
  "format_version": "1.8.0",
  "animations": {
    "animation.golem_guard.smash": {
      "animation_length": 1.5,
      "loop": false,
      "sound_effects": {
        "0.2": {
          "effect": "woosh"
        },
        "0.75": [
          {
            "effect": "impact"
          }
        ]
      },
      "bones": {
        "right_arm": {
          "rotation": {
            "0.0": [0, 0, 0],
            "0.2": [-120, 0, 0],
            "0.75": [30, 0, 0],
            "1.5": [0, 0, 0]
          }
        }
      }
    }
  }
}

Adding Particle Effects and Locators

Particle keyframes are defined in the particle_effects block of an animation, using the same timestamp structure as sounds. However, particles have spatial requirements that sounds do not: you usually want a particle to spawn at a specific bone (like a hand, mouth, or sword tip) rather than at the entity's feet.

To position a particle accurately, you first add a locator to a bone inside your geometry file (.geo.json). Locators are invisible reference points attached to a bone that inherit its transformations. In the animation's particle keyframe, you specify the locator property matching the name in your geometry.

You can also control whether the particle moves with the bone after spawning by setting bind_to_actor. When bind_to_actor is true, the particle emitter stays attached to the locator as the bone moves. When false (or omitted), the particle spawns at the locator's current world position and remains behind in the world as the entity walks away.

If your particle needs dynamic values from the entity, you can use pre_effect_script. This field takes a Molang string that executes on the entity right before the particle spawns, allowing you to set Molang variables (such as variable.charge_level) that the particle's own JSON can read.

FieldWhat it doesNotes
effectThe short name of the particle or sound to play.Must match a key in the client entity's particle_effects or sound_effects block.
locatorNames the geometry locator where the particle spawns.Particles only. If omitted, spawns at the entity's origin (usually between the feet).
bind_to_actorLocks the particle emitter's space to the entity or bone.Particles only. Boolean (true/false). Useful for auras or glowing eyes.
pre_effect_scriptRuns Molang expressions before initializing the particle.Particles only. Variables set here are passed into the particle instance.
RP/animations/golem_guard_particles.animation.json
{
  "format_version": "1.8.0",
  "animations": {
    "animation.golem_guard.smash": {
      "animation_length": 1.5,
      "particle_effects": {
        "0.75": [
          {
            "effect": "dust",
            "locator": "right_hand_locator",
            "bind_to_actor": false
          },
          {
            "effect": "sparks",
            "locator": "right_hand_locator",
            "bind_to_actor": true,
            "pre_effect_script": "variable.spark_scale = 2.0;"
          }
        ]
      }
    }
  }
}

Defining Locators in Geometry

For the locator property in a particle keyframe to work, the locator must exist in the entity's geometry file. Locators are defined inside a bone object using the locators key. Each locator is given a unique name across the model and an offset array in model space [x, y, z].

Because the locator is a child of the bone, it will rotate and pivot whenever that bone is animated. If you use Blockbench to create your models, you can add a locator using the Locator tool in the toolbar, place it inside the target bone group, and position it visually.

RP/models/entity/golem_guard.geo.json
{
  "format_version": "1.12.0",
  "minecraft:geometry": [
    {
      "description": {
        "identifier": "geometry.golem_guard",
        "texture_width": 64,
        "texture_height": 64
      },
      "bones": [
        {
          "name": "right_arm",
          "pivot": [-6, 22, 0],
          "cubes": [
            {
              "origin": [-10, 10, -2],
              "size": [4, 12, 4],
              "uv": [0, 0]
            }
          ],
          "locators": {
            "right_hand_locator": [-8, 10, 0]
          }
        }
      ]
    }
  ]
}

Effects in Animation Controllers

While timeline animations are ideal for timed keyframes (like footsteps at 0.25s and 0.75s), sometimes you just want a sound or particle to fire immediately when an entity enters a specific state (such as beginning a jump or taking damage).

Resource pack animation controllers also support particle_effects and sound_effects arrays inside each state definition. These work identically to animation keyframes—referencing the short names from the client entity file and supporting locator, bind_to_actor, and pre_effect_script—except they trigger once upon entering the state rather than at a specific timestamp.

Which versions this applies to

Applies to Bedrock Edition 1.8.0 and above for animations, and 1.10.0 and above for client entity definitions. All features are stable and do not require any experimental toggles.

When it does not work

What you seeWhyThe fix
The bone animation plays normally, but the sound or particle never triggers and a warning appears in the Content Log stating the effect was not found in the entity.You put the full identifier (such as "minecraft:heart_particle" or "mob.zombie.say") directly inside the animation's effect field instead of registering a short name in the .entity.json file.Define a short name under sound_effects or particle_effects in your client entity description, then put only that short name into the animation file.
Particles spawn at the entity's feet on the ground instead of at the hand or head where the locator was placed.Either the locator field in the animation keyframe has a typo, or the locator was placed in the wrong bone in the geometry file.Check your .geo.json file and verify that the string in the animation's locator property matches the exact key under the bone's locators object (case-sensitive).
Effects at timestamp "0.0" fail to play on the second loop of a looping animation, or get skipped when an animation blends in.Frame rate fluctuations or animation controller state transitions can occasionally step slightly past the exact 0.0 mark on frame zero, causing the engine to miss the 0.0 trigger.Move the keyframe timestamp from "0.0" to a very small offset like "0.01" or "0.05" so it reliably crosses the playback head after the animation starts.
A custom sound triggered by an animation can be heard from infinitely far away across the world.Sounds triggered by animations use the settings in RP/sounds/sound_definitions.json. If a sound is not given a category or spatial attenuation settings, it defaults to non-directional 2D audio.In sound_definitions.json, set the sound's category to "neutral" or "hostile" and ensure max_distance or linear attenuation is configured properly.

Reference, straight from the game files

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