Privacy
Language: Norsk bokmål

Particles

Add particles to a model part: pick a bone, shape each particle, release it on a jump, a swing or a mined block, and make it glow with your own animated picture.

This page has not been translated into Norwegian Bokmål yet, so it is shown in English. Read in English

On this page

A model part can give off particles: a flame that flickers at a wick, sparks on every swing, dust when you land, glitter that only shows in the dark. Punchy! draws each particle itself as a small picture, so you choose its look, its light and the moment it appears.

This page is for pack makers who already have a model part that draws. You will add an emitter to a bone, learn the three ways to write one, control when and how often it fires, make it glow, and give it your own animated picture.

Before you start

  • You need a model part that shows in first person. The model parts overview builds one from three small files, and this page extends that baton.
  • An emitter sits on a bone, so you need the name of a bone in the geometry. Bone names are the name fields in the .geo.json file.
  • Three settings can turn things off. Enable Particle System, Enable Model Parts Glow and Enable Blur Glow At Dark are all on the Effects tab of Punchy’s menu (F8), in the Particles and glow section. Enable Particle System and Enable Model Parts Glow are on by default. Enable Blur Glow At Dark is on by default on Minecraft 1.21.11 and newer, and off by default on 1.20.1, 1.21.1 and 1.21.5, so turn it on there before you test a bright_at_dark particle. See Settings and controls.
  • Emitters in a definition show only while that model part is drawn, and model parts draw in first person only.

Put a flame on the baton

The baton from the overview is one cube. Here it gets a small cap on top, a bone called tip, and a flame at the top of the cap.

  1. Open the Model part test pack from the overview, with its baton.animation.json clip from Make it move with an item clip in place, because the definition below plays it. If you do not have the pack, make it first.

  2. In geo, replace baton.geo.json with the text below. It is the old baton plus a second bone, tip, a small cap on top with its pivot at the top of the cap. A particle starts at the bone you name.

    {
      "format_version": "1.12.0",
      "minecraft:geometry": [
        {
          "description": {
            "identifier": "geometry.baton",
            "texture_width": 16,
            "texture_height": 16
          },
          "bones": [
            {
              "name": "root",
              "pivot": [0, 0, 0],
              "cubes": [
                {
                  "origin": [-2, 0, -2],
                  "size": [4, 12, 4],
                  "uv": {
                    "north": { "uv": [0, 0], "uv_size": [4, 12] },
                    "south": { "uv": [0, 0], "uv_size": [4, 12] },
                    "east": { "uv": [0, 0], "uv_size": [4, 12] },
                    "west": { "uv": [0, 0], "uv_size": [4, 12] },
                    "up": { "uv": [0, 0], "uv_size": [4, 4] },
                    "down": { "uv": [0, 0], "uv_size": [4, 4] }
                  }
                }
              ]
            },
            {
              "name": "tip",
              "pivot": [0, 14, 0],
              "cubes": [
                {
                  "origin": [-1.5, 12, -1.5],
                  "size": [3, 2, 3],
                  "uv": {
                    "north": { "uv": [0, 0], "uv_size": [3, 2] },
                    "south": { "uv": [0, 0], "uv_size": [3, 2] },
                    "east": { "uv": [0, 0], "uv_size": [3, 2] },
                    "west": { "uv": [0, 0], "uv_size": [3, 2] },
                    "up": { "uv": [0, 0], "uv_size": [3, 3] },
                    "down": { "uv": [0, 0], "uv_size": [3, 3] }
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  3. In definitions, replace baton.json with the text below. It keeps the idle clip from the overview and adds one emitter under particles.

    {
      "items": ["minecraft:stick"],
      "geo": "mypack:punchy/model_parts_items/geo/baton.geo.json",
      "texture": "minecraft:block/oak_planks",
      "animations": [
        "mypack:punchy/model_parts_items/animations/baton.animation.json"
      ],
      "modelPartsAnimation": [
        { "type": "idle", "name": "baton_sway" }
      ],
      "particles": [
        {
          "type": "minecraft:flame",
          "bone": "tip",
          "chance": 0.4,
          "life": 0.6,
          "size": 0.04,
          "size_end": 0.01,
          "spread": [0.01, 0, 0.01],
          "velocity": [0, 0.01, 0]
        }
      ]
    }
  4. In the world, press F3+T and hold a stick in first person. A flame flickers at the top of the cap.

Video coming soon

The flame starts at the tip bone, at the top of the cap.

Read the emitter line by line. type chooses the picture, bone where it starts, chance makes only 4 in 10 ticks release a particle, life is how many seconds a particle lives, size and size_end shrink it from start to end, spread scatters the start a little and velocity gives it a small push upward. Every key is explained in the reference below.

Make the same emitter in Punchy! Studio

The Model workspace edits every key with its default shown, previews the particles on the stage and shows them in your hand through the live link.

  1. Press F6 in a world in first person, pick First Person and open the Model tab. Open your part with File > Open Model Part… (Ctrl+O), or start one with File > New Model Part.
  2. Switch the stage to Particles mode with the header button or the 3 key.
  3. Choose Add > Add Emitter. The emitter appears under Particles in the outliner.
  4. In the properties, open the Particles tab. Set Type to minecraft:flame and Bone to tip. The panels are Emission, Timing and Triggers, Look, Space and Motion and Script. Keys you have not written show Punchy!‘s default greyed out, and the Script panel shows the emitter as it will be written.
  5. Set Chance to 0.4, Life to 0.6, Size to 0.04 and End Size to 0.01.
  6. Choose View > Live Link with Punchy (Ctrl+L) and hold the item. Punchy! draws your unsaved emitter in your hand.
  7. At the bottom, the Trigger Test panel has the switches Walking, Sprinting, Swimming and Falling and the buttons Jump, Land, Attack, Mine and Use. Press them to see which emitters fire, and Clear to reset.
  8. Choose File > Export Model Part… (Ctrl+E) to write the pack.

Video coming soon

Add Emitter, set Type and Bone, and watch it through the live link.

Three ways to write an emitter

An emitter is a JSON object, a script string or a bare particle id. They all end up as the same thing. In the list below, the first three emitters are the same one written three ways. An example further down that shows a single emitter object is one entry of the particles list in a definition such as baton.json: to try it, add it to that list.

{
  "particles": [
    {
      "type": "minecraft:flame",
      "bone": "tip",
      "chance": 0.4,
      "velocity": [0, 0.01, 0],
      "triggers": { "walking": true }
    },
    "particle(type=minecraft:flame, bone=tip, chance=0.4, velocity=0:0.01:0, trigger_walking=true)",
    "type=minecraft:flame, bone=tip, chance=0.4, velocity=0:0.01:0, trigger_walking=true",
    "minecraft:flame"
  ]
}
  • Object. Vectors are lists of three numbers. Booleans are true and false. Set "enabled": false to switch an emitter off without deleting it. triggers and on_animation are small objects with their own keys, explained below. Every key on this page works in an object except the long glow names emissive_strength, emissive_blur, emissive_clamp and their emission_ spellings, which are read in script strings only. Write strength, blur and clamp in an object.
  • Script string. Keys are key=value pairs separated by commas. A vector is three numbers joined with colons, as in velocity=0:0.01:0. A list is joined with |. Because a comma ends a pair, no value can contain one. Every key in the reference below works in a script string except triggers and enabled, which are for objects only. In a script use the trigger_ keys and leave enabled out. A string that starts with particle( and ends with ) is used as it is. Without that wrapper, a string that has an = in it is wrapped for you.
  • Bare id. A string with no = becomes particle(type=<id>): one particle per tick at the hand, because no bone is named. It takes no other keys, so use an object or a script string when you need any. An id that is not valid is dropped.

Keys are not case sensitive in a script, and a value that cannot be read keeps the default. Keys the particle code does not know are ignored without a message.

Where emitters can be written

  • In the definition. Five keys are read, in this order: particle, particles, particle_effects, particle_emission and particleEmission. Each holds one emitter or a list, and the lists are joined.
  • In a compat rule. The item entries and the itemSpecific entries of a compat file read particle_emitters first and then the same four names. An item that a model part covers draws the definition’s emitters first and the compat emitters after them. An item without a model part gets the compat emitters too. On such an item an emitter can also use "target": "item_center" for the middle of the item, or "target": "block_face" for a face of the held item’s box, chosen by the block you mine or look at (or by face). On an item that has a model part, particles start at a bone, and only "target": "world_block_face" moves the start to the block.
  • Per enchantment. enchantment_variants in an itemSpecific entry adds emitters while the held item has certain enchantments. See Enchantment variants.

An emitter lives under its exact text. Two identical strings are one emitter, and changing a single character restarts it.

When an emitter fires

Every frame, Punchy! asks each emitter the same questions in the same order.

  1. Is its window still open? If you set emit_for, the emitter stops for good that many seconds after it started. This holds for pulse emitters as well as steady ones.
  2. Do its triggers pass? An emitter with no triggers always passes. See trigger families and combining them. A mined block filter, if you wrote one, must pass too.
  3. Does it have a pulse trigger? A pulse trigger is a single event: a jump, a landing, a swing, a use, a mining hit or a moment in a clip. Each event releases one burst of count particles.
  4. Is it pulse only? An emitter whose only triggers are pulses stops here. Any other emitter then runs its steady clock, the one described under burst, rate and interval.
  5. Each release rolls chance once and then spawns count particles.

An emitter with no triggers at all has no pulse and no condition, so it runs on the steady clock all the time. That is how the flame above works.

Trigger families

Triggers

FamilyKeysKindTrue or released when
Jumptrigger_jump, jumpPulseYou take off from the ground. One burst per jump.
Landingtrigger_landing, landingPulseYou touch the ground. One burst per landing.
Attackingtrigger_attacking, attacking, attackPulseYou swing or press attack with this hand.
Usingtrigger_use_item, use_item, useitemPulseYou use the item with this hand.
Clip momenton_animation_name with on_animation_timecode, or on_animationPulseThe named item clip, or the arm clip of that name, passes the time you give.
Miningtrigger_mining, miningPulse and steadyYou mine a block with your main hand. Releases a burst for each mining event, and the steady clock runs while you keep mining.
Walkingtrigger_walking, walkingSteadyYou are on the ground and moving or holding a movement key, and not sprinting or swimming.
Sprintingtrigger_sprinting, sprintingSteadyYou are sprinting and moving or holding a movement key.
Swimmingtrigger_swimming, swimmingSteadyYou are swimming or in water.
Fallingtrigger_falling, fallingSteadyYou are in the air and dropping. trigger_falling_min_blocks sets how far you must have fallen.
Biomestrigger_biomes, biomes, biomeSteadyThe biome at your feet is in the list.

A steady trigger is a condition: the emitter fires on its clock while the condition is true. A pulse is an event: the emitter fires once for each event. The jump and landing pulses last two ticks, and each event still counts once.

Written in an object, triggers go inside a triggers object:

{
  "type": "minecraft:crit",
  "bone": "tip",
  "count": 6,
  "triggers": {
    "attacking": true,
    "falling": { "min_blocks": 3 },
    "biome": "minecraft:desert|minecraft:badlands"
  }
}

In that object the keys are jump, landing, walking, sprinting, swimming, attacking, mining, falling and biome. Each flag is true or false. falling can also be an object with min_blocks, and biome can be one id, a #tag, or several joined with |. To trigger on using the item, write "use_item": true next to triggers, not inside it.

Combine triggers with all and any

By default an emitter needs all of its triggers to be true at the same moment. That is right for walking with a biome: walk in a desert. It is wrong for two events that never overlap. A jump pulse and a landing pulse are never both on, so an emitter with both and the default mode never fires.

Set trigger_mode to any to fire when one of the triggers is true. In an object write it beside triggers:

{
  "type": "minecraft:smoke",
  "bone": "root",
  "count": 6,
  "triggers": { "jump": true, "landing": true },
  "trigger_mode": "any"
}

trigger_mode, trigger_logic, triggers_mode and triggers_logic mean the same. Only any and or switch to any, in any case. Every other value means all.

In a compat rule, triggers can also be a list of trigger names or one name, and a list means any. That is a compat-rule shortcut. In a definition, write triggers as an object.

A clip moment is a pulse that does not count as a condition. If you list other triggers next to it, those must pass for the clip moment to release anything.

The clip moment

on_animation fires an emitter at an exact time of an item clip, so a puff can leave at the moment the sword hits. Give it the clip’s name and a time in seconds:

{
  "type": "minecraft:crit",
  "bone": "tip",
  "count": 8,
  "on_animation": { "name": "baton_sway", "timecode": 1.0 }
}

The same thing in a script is [email protected]. The name is matched without regard to case against the clip that is playing for that hand. The burst happens once each time the clip passes the time, and again on every loop of a looping clip. Use a time above 0: a time of 0 is passed only when a clip loops around, never when it first starts. A name with no time sets no trigger.

The same key also watches the first person clip that moves your arms, the one a compat rule plays. The emitter fires when an arm clip of that name, or of that name with _right or _left after it, passes the time.

Burst, rate and interval

Three keys decide how an emitter releases once it is allowed to.

  • count (also burst) is how many particles one release spawns, from 0 to 120.
  • rate (also rate_per_sec, pps) releases that many times a second, up to 120. Punchy! counts the rate up each frame and releases once for every whole unit it has gathered, at most 24 times in one frame. What is left over carries on to the next frame.
  • emit_interval (also emit_every_seconds, interval_seconds, spawn_every_seconds) releases once every that many seconds, up to 60. When both are set, the interval wins and the rate is not used.

With neither, a steady emitter releases once per game tick, about 20 times a second. With a count of 1 that is 20 particles a second, and with the default life of 1.1 seconds about 22 alive at once. Use chance, a rate or an emit_interval to thin it out.

{
  "type": "minecraft:smoke",
  "bone": "tip",
  "rate": 6,
  "count": 2,
  "life": 1.5
}

That is 6 releases a second of 2 particles each, 12 particles a second.

  • A burst is a pulse trigger with a count: trigger_jump with count: 8 throws 8 particles per jump.
  • A stream is a steady emitter with a rate or emit_interval.
  • A pulse-only emitter, one whose triggers are only pulses, ignores rate and emit_interval. It releases once per event.
  • chance is rolled once for each release, not for each particle. At 0.3 three releases in ten spawn their whole count.
  • emit_for (also duration) stops the emitter for good after that many seconds, from 0 to 30. 0 means no limit. The clock runs from the moment the emitter starts. It does not restart by itself, and changing the emitter’s text restarts it.
  • t (0 to 1, default 1) and ease (linear, ease_in_quad, ease_out_quad or ease_in_out_cubic) scale count and the starting velocity. A count of 10 with t of 0.5 and the linear ease spawns 5. ease also shapes how the size and alpha move over a particle’s life. Any other ease value plays as linear.

Recipes

Here is one definition with five emitters that use the triggers above. It is the baton from the start of this page with a flame, dust, sparks, mining sparks and a puff on the sway. It uses the baton.geo.json from the start of this page and the baton.animation.json from the overview.

{
  "items": ["minecraft:stick"],
  "geo": "mypack:punchy/model_parts_items/geo/baton.geo.json",
  "texture": "minecraft:block/oak_planks",
  "animations": [
    "mypack:punchy/model_parts_items/animations/baton.animation.json"
  ],
  "modelPartsAnimation": [
    { "type": "idle", "name": "baton_sway" }
  ],
  "particles": [
    {
      "type": "minecraft:flame",
      "bone": "tip",
      "chance": 0.4,
      "life": 0.6,
      "size": 0.04,
      "size_end": 0.01,
      "spread": [0.01, 0, 0.01],
      "velocity": [0, 0.01, 0]
    },
    {
      "type": "minecraft:smoke",
      "bone": "root",
      "count": 6,
      "life": 0.8,
      "size": 0.05,
      "spread": [0.04, 0.01, 0.04],
      "triggers": { "jump": true, "landing": true },
      "trigger_mode": "any"
    },
    {
      "type": "minecraft:crit",
      "bone": "tip",
      "count": 8,
      "life": 0.4,
      "size": 0.03,
      "spread": [0.03, 0.03, 0.03],
      "triggers": { "attacking": true }
    },
    {
      "type": "minecraft:crit",
      "target": "world_block_face",
      "count": 3,
      "emit_interval": 0.12,
      "life": 0.5,
      "size": 0.03,
      "triggers": { "mining": true },
      "trigger_block": "#minecraft:mineable/pickaxe"
    },
    {
      "type": "minecraft:end_rod",
      "bone": "tip",
      "count": 4,
      "life": 0.8,
      "size": 0.03,
      "spread": [0.02, 0.02, 0.02],
      "on_animation": { "name": "baton_sway", "timecode": 1.0 }
    }
  ]
}
  1. Flame. No triggers, so it runs on the steady clock all the time.
  2. Dust. Two pulses joined with trigger_mode: any, so it fires on takeoff and on landing.
  3. Sparks. A single pulse. Each swing throws 8.
  4. Mining sparks. The target world_block_face puts the particles on the face of the block you are mining. trigger_block limits it to blocks of that tag, and emit_interval makes a stream while you keep mining. Read the mined block below.
  5. Puff. The clip baton_sway lasts 2 seconds, so this fires halfway through each loop.

Video coming soon

Four triggers on one baton.

The mined block

Three groups of keys tie an emitter to the block you are mining.

  • Target. target (also emit_target, target_type, emit_target_type) is bone by default, so particles start at a bone. Set it to world_block_face (also world_face, world_hit_face, target_block_face) and they start on a face of the block instead. The block is the one you are mining, or for a moment after, or else the one under your crosshair.
  • Face. face (also block_face, emit_face, mining_face) picks which face: hit (the face you hit, the default), random, opposite, up, down, north, south, west or east. face_region (also face_bounds, surface, surface_region, surface_bounds) limits where on that face particles start: hit or exact for the point you hit, or four numbers u:v:u:v from 0 to 1. Without it, particles start at the point you hit (the same as hit) when Punchy! knows it, and anywhere on the face otherwise. Give four numbers that do not cover the whole face to scatter them over that part.
  • Filter. trigger_block (also trigger_blocks, block, blocks, mining_block, mining_blocks) is a list of block ids or #tags separated by | or ;. trigger_block_state (also block_state, mining_block_state) lists required state values such as waterlogged=false|facing=north, separated by | or ;. The names and values ignore case.

A filter or a mined face target is a condition of its own. An emitter that has one fires, on its steady clock too, only while such a block exists and passes the filter. Without a mining trigger a world_block_face emitter fires for any block you look at, so add "triggers": { "mining": true } when you mean mining. Write several blocks as one string with |. A list of exactly three entries in an object is read as a vector.

Light and glow

By default particles are fully lit and get a glow layer, so they stay bright in the dark. Write "fullbright": false to make them follow the world’s light. Four keys change how they are lit.

  • fullbright (also full_bright) is on by default. The particle ignores the dark and gets a glow layer drawn over it. It needs Enable Model Parts Glow. Write "fullbright": false for a dull ash or smoke that follows the world’s light. Types with leaf or leaves in the name start with fullbright and glow off.
  • full_day_light (also fulldaylight, day_fullbright, daylight_fullbright) makes the particle fully lit only in the overworld during the day. Write "fullbright": false as well, or the default full light also stays on at night. It needs Enable Model Parts Glow.
  • bright_at_dark (also blur_glow_at_dark, dark_bright, bright_in_dark, bright_at_night, night_bright, full_night_light, night_fullbright) makes a firefly. In bright places the particle is almost invisible, and the darker the world gets, the more of its alpha and its glow it shows. It turns fullbright off unless you also write fullbright. It shows only while Enable Blur Glow At Dark is on, and it does not need Enable Model Parts Glow.
  • light_percent (also light_pct, light, brightness_percent) mixes the world’s light toward full light, from 0 to 100. Without it the particle uses the world’s light or, with fullbright, full light. When you write it, it replaces the full light of fullbright, and anything below 100 also turns the glow layer off.

The glow layer is tuned with three more keys:

  • strength (also emissive, emissive_strength, emission_strength) is how strong the layer is, from 0 to 100. The default is 100, a fully bright copy of the picture. From 0 to 15 the layer’s brightness is the value divided by 15. Above 15 it is fully bright and, with blur, a halo grows around it up to 100.
  • blur (also emissive_blur, emission_blur) adds that soft halo. It is off by default.
  • clamp (also emissive_clamp, emission_clamp) is on by default and clamps the glow layer’s picture at the edges.

glow_target_menu is a flag that hides the particle whenever Enable Blur Glow At Dark is off, the same as bright_at_dark does.

{
  "type": "minecraft:end_rod",
  "bone": "tip",
  "rate": 3,
  "life": 1.2,
  "size": 0.035,
  "tint": [255, 200, 80],
  "strength": 100,
  "blur": true,
  "bright_at_dark": true
}

Glow only appears when the layer has something to draw. fullbright false with a strength alone does nothing, because the layer is drawn only for fullbright, full_day_light or bright_at_dark.

Video coming soon

A dark glow particle fades in as the light drops.

Additive glow

Write "additive": true and the particle’s light adds to what is behind it instead of covering it. The dark edges of a glow picture then stop darkening the sky around it. It is off by default, and in a script string you write additive=true.

{
  "itemSpecific": {
    "minecraft:golden_sword": {
      "particle_emitters": [
        {
          "type": "minecraft:end_rod",
          "texture": "mypack:particle/halo",
          "target": "item_center",
          "size": 0.9,
          "alpha": 0.9,
          "lock_in_model": true,
          "fullbright": true,
          "additive": true
        }
      ]
    }
  }
}

That is a compat rule, as in Compat rules, and the picture halo.png is one you draw, as described under Draw the pictures used on this page. The same key works in the particles of a definition. A halo that is pinned in the model with lock_in_model is drawn whole over the item, not cut off where the item is in front of it. Other additive particles keep the depth test, so whatever is in front of them hides them. Particles without additive are drawn as before. Punchy! Studio does not have a field for additive. It keeps the key as written, its Lint panel does not flag it, and its preview draws the glow the usual way.

Light and blending by item

A shading.json file in a pack can set, for one held item, whether its model part is drawn bright in the dark (fullbright), lit evenly on every face (shading: false), and whether every particle drawn while that item is held is additive (additive). A rule for the held item decides over the particle’s own additive key, and the last matching rule wins. A file that Punchy! cannot read as JSON gives no rules, without a message.

{
  "rules": [
    { "item": "minecraft:lava_bucket", "fullbright": true },
    { "item": "minecraft:water_bucket", "shading": false },
    { "item": "minecraft:diamond_sword", "additive": true }
  ]
}

The file goes in assets/<namespace>/punchy/model_parts_items/. Its keys and defaults are in the flags reference, and Light a whole model part on the geometry page covers the same file from the model’s side.

Textures and animated textures

A particle is one square picture. The picture fills the whole square, and its colors are multiplied by tint.

Choose the picture

With a texture key, the particle uses that picture. The value is a resource id, and an id without a namespace means minecraft. Punchy! tries these paths in the id’s namespace and uses the first that exists:

  1. the id with .png added
  2. textures/ in front of the id
  3. textures/particle/ in front of the id
  4. textures/ in front of the id, when the id starts with particle/

The second and third are skipped when the id already starts with textures/. If none exists, the id with .png added is used as it is. So a picture saved as assets/mypack/textures/particle/glow.png is written "texture": "mypack:particle/glow" or "texture": "mypack:glow".

Without a texture key, type decides, in this order:

  1. A particle definition assets/<namespace>/particles/<name>.json in the pack or the game. The first picture in its textures list is used.
  2. The picture textures/particle/<name>.png, or <name>_0.png. A _particle ending on the name and a particle/ start on it are dropped when the full name finds nothing.
  3. By the name: soul gives soul_0, smoke gives generic_0, and campfire or ash gives ash.
  4. The flame picture.

So minecraft:flame draws the game’s flame, and a type of your own, such as mypack:spark, draws the first picture of assets/mypack/particles/spark.json or assets/mypack/textures/particle/spark.png.

tint (also color, rgb) is three numbers for red, green and blue, from 0 to 1 or from 0 to 255. If any number is above 1, all three are read as 0 to 255. Draw a particle white and tint it for any color. With no tint it is white, and types with falling_lava, landing_lava, dripping_lava or dripstone_lava in the name start orange.

Make an animated particle

Name the frames as a numbered set and point texture at the first.

  1. Make the pictures spark_0.png, spark_1.png, spark_2.png, spark_3.png and so on in assets/mypack/textures/particle/. Number them without gaps. Punchy! counts up from _1 until it finds a file missing, and stops at _63, so the longest strip has 64 frames. The example below uses six frames of 8 by 8 pixels, drawn as shown under Draw the pictures used on this page.
  2. Add the emitter below to the particles list in baton.json, with "texture": "mypack:particle/spark_0". The id must end in _0.
  3. Press F3+T and watch the particle play the frames in order.

The strip plays at 12 frames a second from the moment the particle appears and loops for as long as the particle lives. Set life to the strip’s length divided by 12 to play it once. A type that has no texture key animates in the same way when its picture is named <name>_0.png.

{
  "type": "mypack:spark",
  "texture": "mypack:particle/spark_0",
  "bone": "tip",
  "rate": 4,
  "life": 0.5,
  "size": 0.04,
  "tint": [255, 220, 120]
}

Video coming soon

The frames spark_0 to spark_5 play at 12 frames a second.

In Punchy! Studio, the Particles tab has a Project Image field that draws the emitter with one of the model’s images. Dropping a PNG on the window while an emitter is selected makes it that emitter’s picture. The picture is saved in the project and exported with it.

Draw the pictures used on this page

Three sets of pictures on this page are yours to draw, in any pixel editor. Draw them white on a see-through background, because tint colors a white picture any color. Save them all in assets/mypack/textures/particle/.

The six frames spark_0.png to spark_5.png are 8 by 8 pixels each. Each letter below is one pixel: W is solid white, w is white that is partly see-through, and a dot is a see-through pixel. Frames 0 and 5 look alike, as do 1 and 4, and 2 and 3, so the strip grows to full size in the middle and shrinks back.

Frame 0   Frame 1   Frame 2   Frame 3   Frame 4   Frame 5
........  ........  ....w...  ....w...  ........  ........
........  ....w...  .w..w.w.  .w..w.w.  ....w...  ........
....w...  ....w...  ....w...  ....w...  ....w...  ....w...
..www...  .wwww...  wwwWW...  wwwWW...  .wwww...  ..www...
...www..  ...wwww.  ...WWwww  ...WWwww  ...wwww.  ...www..
...w....  ...w....  ...w....  ...w....  ...w....  ...w....
........  ...w....  .w.w..w.  .w.w..w.  ...w....  ........
........  ........  ...w....  ...w....  ........  ........

The pinned glow uses glow.png and the additive glow uses halo.png. Make each a square picture, for example 32 by 32 pixels: a round white dot that is solid in the middle and fades to fully see-through before the edge. A particle’s picture fills its whole square, so a soft round dot gives a soft round glow.

Motion, orbits and pinned glows

velocity pushes a particle at the start. After that each tick drag keeps that share of the speed (0.93 by default, from 0 to 1.2) and gravity (from -2 to 2) adds to the vertical speed. A positive gravity pushes the way a positive y velocity does, and a negative one pulls the other way. Both work the same at any frame rate.

A particle that follows its bone ignores blocks. A world_block_face particle moves through the world and can collide with blocks.

An orbit circles each particle around a center. Set motion to orbit (also orbital or circle) and give it a radius and a speed:

{
  "type": "minecraft:end_rod",
  "bone": "tip",
  "motion": "orbit",
  "orbit_radius": 0.06,
  "orbit_speed": 1.5,
  "orbit_axis": [0, 1, 0],
  "count": 3,
  "emit_interval": 0.4,
  "life": 1.2,
  "size": 0.025
}

The three particles of each release start evenly spaced around the circle. orbit_speed is in turns per second, and a negative number turns the other way. An orbit replaces the velocity.

A pinned glow stays on its bone. Set lock_in_model to true for a halo or a lit gem. The particle does not move, never ends and ignores drag, gravity and the end size and alpha, and every new release of the emitter replaces the old particle instead of adding to it. In a definition it needs a bone that exists. In a compat emitter on an item without a model part it pins to the middle of the item, as in the halo under Additive glow. It never plays an animated texture. Add "additive": true for a halo that adds light, as in Additive glow. The picture glow.png used in the emitter below is drawn as described under Draw the pictures used on this page.

{
  "type": "minecraft:end_rod",
  "texture": "mypack:particle/glow",
  "bone": "tip",
  "lock_in_model": true,
  "size": 0.09,
  "alpha": 0.6,
  "tint": [255, 190, 90]
}

Silence particles during a clip

An animation entry can switch every emitter off while it is the active one. Put "disable_all_particles": true in a modelPartsAnimation entry, as in { "type": "useItem", "name": "baton_use", "disable_all_particles": true }. The particles of that arm are cleared and stay off until the entry ends. See Item clips for the entries.

Every emitter key

Key names in one row are aliases of each other. A value that cannot be read keeps the default. Numbers outside the range are brought into it. A vector is [x, y, z] in an object and x:y:z in a script. A distance is in blocks, where 1 block is 16 model pixels, so 0.0625 is one pixel.

Where it starts

Type, bone and place
KeyTypeDefaultWhat it doesExample
typeparticle idminecraft:critThe particle's name. Punchy! draws its own picture for it, and type picks that picture. See Textures. Any id is fine, so you can make one up for your own particle definition."minecraft:flame"
bonetextnoneThe bone the particle starts at. Punchy! looks for the exact name, then the name ignoring case, then, for a name with grip or item in it, a bone of that kind on the holding arm's side. With no match the particle starts at your hand."tip"
target, emit_target, target_type, emit_target_typetextboneWhere the particle starts. bone starts it at the bone. world_block_face starts it on a face of the mined block (see the mined block). On an item without a model part, a compat emitter can also use item_center (the middle of the item) and block_face (a face of the held item's box, chosen by the block you mine or look at). On an item with a model part, only world_block_face moves the start away from the bone."world_block_face"
offsetvector0:0:0Moves the start from the bone, in blocks, in the space of offset_space.[0, 0.0625, 0]
spreadvector0:0:0A random scatter of the start, up to this far either way on each axis. The starting velocity is scattered by a quarter of it.[0.02, 0.02, 0.02]
velocityvector0:0:0The starting push, in the space of velocity_space. It is scaled by t and ease like count.[0, 0.01, 0]

How many and how often

Count, timing and chance
KeyTypeDefaultWhat it doesExample
count, burstwhole number1Particles per release, 0 to 120, after t and ease scale it.6
chancenumber1The chance, 0 to 1, that a release spawns anything. Rolled once per release.0.4
rate, rate_per_sec, ppsnumber0Releases per second for a steady emitter, 0 to 120. At most 24 releases per frame.6
emit_interval, emit_every_seconds, interval_seconds, spawn_every_secondsseconds0Seconds between releases for a steady emitter, 0 to 60. Wins over rate when above 0.0.12
emit_for, durationseconds0The emitter stops for good this many seconds after it started, 0 to 30. 0 means no limit.5
tnumber1A share, 0 to 1, put through ease and multiplied into count and velocity. 0 spawns nothing.0.5
easetextlinearlinear, ease_in_quad, ease_out_quad or ease_in_out_cubic. Shapes the effect of t, and how size and alpha change over a particle's life. Any other value plays as linear."ease_out_quad"

Triggers

When it fires
KeyTypeDefaultWhat it doesExample
trigger_jump, jumptrue or falsefalseA pulse at takeoff.
trigger_landing, landingtrue or falsefalseA pulse on landing.
trigger_attacking, attacking, attacktrue or falsefalseA pulse for each swing or attack of this hand.
trigger_use_item, trigger_useitem, use_item, useitemtrue or falsefalseA pulse each time this hand uses the item.
trigger_mining, miningtrue or falsefalseA pulse for each mining event, and the steady clock runs while you mine. Main hand only.
trigger_walking, walkingtrue or falsefalseSteady. On the ground, moving, not sprinting and not swimming.
trigger_sprinting, sprintingtrue or falsefalseSteady. Sprinting and moving.
trigger_swimming, swimmingtrue or falsefalseSteady. Swimming or in water.
trigger_falling, fallingtrue or falsefalseSteady. In the air and dropping.
trigger_falling_min_blocks, falling_min_blocks, min_fall_blocks, min_blocksnumber0The shortest fall, in blocks, from 0 to 512, before falling counts.3
on_animation_name, trigger_animation_name, animation_nametextnoneThe item clip to watch, matched without regard to case. It needs a timecode to take effect."baton_sway"
on_animation_timecode, trigger_animation_timecode, animation_timecode, timecodesecondsunsetThe time in that clip at which one burst is released. Use a time above 0.1.0
on_animation, trigger_on_animationtextName and time in one value, name@seconds. In an object it can also be { "name": ..., "timecode": ... }."baton_[email protected]"
trigger_biomes, biome_names, biomes, trigger_biome, biomelistnoneBiome ids or #tags separated by | or ;. True when the biome at your feet is in the list."minecraft:desert|#minecraft:is_ocean"
trigger_mode, trigger_logic, triggers_mode, triggers_logictextallany or or fires when one trigger is true. Anything else needs all of them."any"
triggersobjectObject form only. The trigger flags as jump, landing, walking, sprinting, swimming, attacking, mining, falling (a flag, or { "min_blocks": 3 }) and biome.
enabledtrue or falsetrueObject form only. false switches the emitter off.

Look

Size, color and life
KeyTypeDefaultWhat it doesExample
life, lifetimeseconds1.1How long a particle lives, 0.05 to 60.0.6
sizenumber0.028Half the width of the square at the start, 0.001 to 2. Double it for a particle twice as wide.0.04
size_endnumber0.006Half the width at the end of life, 0 to 2.0.01
alphanumber0.95Opacity at the start, 0 to 1. A number above 1 is read as 0 to 255.0.8
alpha_endnumber0Opacity at the end of life. Same range as alpha.0
dragnumber0.93The share of speed kept each tick, 0 to 1.2.0.9
gravitynumber0Added to the vertical speed over time, -2 to 2.0.05
tint, color, rgbvectorwhiteRed, green and blue, each 0 to 1, or each 0 to 255 when any is above 1.[255, 200, 80]
textureresource idthe type's pictureThe picture to draw. See Textures. A name ending in _0 plays as an animation."mypack:particle/glow"

Light and glow

Light and glow
KeyTypeDefaultWhat it doesExample
fullbright, full_brighttrue or falsetrueFull light and a glow layer. false for types with leaf or leaves in the name unless you write it. Needs Enable Model Parts Glow.
full_day_light, fulldaylight, day_fullbright, daylight_fullbrighttrue or falsefalseFull light only in the overworld by day. Needs Enable Model Parts Glow.
bright_at_dark, blur_glow_at_dark, dark_bright, bright_in_dark, bright_at_night, night_bright, full_night_light, night_fullbrighttrue or falsefalseFades in and glows as the world gets dark. Turns fullbright off unless you write it. Shows only while Enable Blur Glow At Dark is on.
additivetrue or falsefalseThe particle's light adds to what is behind it, so the dark edges of a glow picture do not darken it. A shading.json rule for the held item decides over this key. A glow with lock_in_model is drawn whole over the item.true
glow_target_menutrue or falsefalseHides the particle whenever Enable Blur Glow At Dark is off.
light_percent, light_pct, light, brightness_percentnumberworld lightMixes the world's light toward full light, 0 to 100. Below 100 the glow layer is not drawn.100
strength, emissive, emissive_strength, emission_strengthwhole number100The glow layer's strength, 0 to 100. 0 for types with leaf or leaves in the name unless you write it. Use strength in an object.60
blur, emissive_blur, emission_blurtrue or falsefalseAdds a soft halo. Use blur in an object.
clamp, emissive_clamp, emission_clamptrue or falsetrueClamps the glow layer's picture at the edges. Use clamp in an object.

Space and motion

Space, orbit and pinning
KeyTypeDefaultWhat it doesExample
offset_space, space_offsettextboneThe space offset is read in: bone, world, camera, block or block_face. face and blockface mean block_face.
velocity_space, space_velocity, vel_spacetextworldThe space velocity is read in. The same values as offset_space.
ignore_model_rotation_for_emit, ignore_model_rotation, emit_ignore_model_rotation, no_model_rot_emittrue or falsetrueThe bone's turning is ignored when placing the start, so an offset stays put while the bone sways. false turns the offset with the bone.
motion, movement, motion_modetextlinearlinear (also default, drag) or orbit (also orbital, circle).
motion_space, orbit_spacetextboneThe space the orbit axis and center are read in.
orbit_radius, radiusnumber0The orbit's radius in blocks, 0 to 4.0.06
orbit_speed, angular_speednumber0Turns per second, -20 to 20. Negative turns the other way.1.5
orbit_axis, axisvector0:1:0The axis the particle circles.[0, 1, 0]
orbit_phase, phasenumber0The starting angle in degrees. The particles of one release are spread evenly from it.90
orbit_center, center_offset, motion_centervector0:0:0Moves the orbit's center, in motion_space.[0, 0.1, 0]
lock_in_model, lock_model, locked_in_model, pin_in_model, pinned_in_modeltrue or falsefalsePins the particle to its bone with no motion and no end. A new release replaces the old one. In a definition it needs a bone that exists; in a compat emitter on an item without a model part it pins to the middle of the item.

The mined block

Block faces and filters
KeyTypeDefaultWhat it doesExample
face, block_face, emit_face, mining_facetexthitThe face for a world_block_face emitter: hit (also impact, clicked), random, opposite (also back, reverse), up, down, north, south, west or east.
face_region, face_bounds, surface, surface_region, surface_boundstexthitWhere on the face: hit, impact or exact for the hit point (the default), or four numbers u:v:u:v from 0 to 1 that do not cover the whole face to scatter particles over that part."0.25:0.25:0.75:0.75"
trigger_block, trigger_blocks, block, blocks, mining_block, mining_blockslistnoneBlock ids or #tags separated by | or ;. The mined block must be one of them."minecraft:stone|#minecraft:logs"
trigger_block_state, block_state, mining_block_statelistnoneRequired states as name:value or name=value, separated by | or ;. Names and values ignore case."waterlogged=false"

Things that go wrong

  • The particles start at your hand. The bone name has no match. Check it against the geometry. A bone with no name, or no match, starts at the hand rather than being dropped.
  • Nothing shows. Check Enable Particle System. An empty particle() makes no emitter, and "enabled": false removes one. A count of 0, a chance of 0 or a t of 0 spawns nothing.
  • A particle appears 20 times a second. An emitter with no triggers, rate or emit_interval releases once per game tick. Add chance, a rate or an emit_interval.
  • A pulse emitter ignores my rate. An emitter whose triggers are only pulses fires once per event and does not use rate or emit_interval. Add a steady trigger and set trigger_mode to any, and the stream runs while that trigger holds. Or use mining, which is both. In the default mode a pulse and a steady trigger must both be true at once, and some pairs never are: jump needs you off the ground and walking needs you on it.
  • An emitter with jump and landing never fires. The default mode needs both together. Add "trigger_mode": "any".
  • My triggers are ignored in a definition. Write triggers as an object with named flags, and put trigger_mode beside it. The list form is for compat rules. In a definition a list or a plain string is ignored, and with it ignored the emitter runs all the time.
  • An emitter runs all the time and I gave it triggers. An empty triggers object, a triggers object whose flags are all false, or an on_animation with a name and no timecode sets no trigger, so the emitter runs on the steady clock. In a compat rule, a triggers list whose names are all misspelled does the same, because the list sets any mode first and then finds nothing to wait for.
  • A script value breaks the emitter. A comma ends a pair in a script. Join lists with |, and vectors with colons.
  • A list of three became a vector. In an object, an array of exactly three values is read as x:y:z. Write biome and block lists as one string joined with |.
  • A mining emitter fires when I only look at a block. A world_block_face target counts the block under the crosshair. Add "triggers": { "mining": true }.
  • A clip moment never fires at 0. A time of 0 is passed only when a looping clip wraps around. Use a time above 0, and check the clip’s name.
  • There is no glow. Check Enable Model Parts Glow. Check that fullbright, full_day_light or bright_at_dark is on, and that light_percent is unset or 100. A bright_at_dark particle also needs Enable Blur Glow At Dark. On 1.20.1, 1.21.1 and 1.21.5 this setting starts off.
  • My picture does not show. Check the path against the list under Choose the picture. The file must be a PNG. If no file matches, the id with .png added is used as written.
  • The animation stops early or does not play. The first frame is named _0, the frames count up without a gap, and the id in texture ends in _0. A pinned particle never animates. Punchy! counts the frames of a strip once per session, so after you add frames to a strip that has already played, restart the game.
  • Particles stop while I use the item. Model parts are skipped while you draw a bow, eat or block, unless the definition has an entry for that use. See the overview.
  • A particle limit. Each release spawns at most 120 particles, and rate is capped at 120 a second.

Last checked against the mod on 29 October 2026.