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.
-
Punchy! Studio
This page has not been translated into Vietnamese 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
namefields in the.geo.jsonfile. - 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_darkparticle. 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.
-
Open the
Model part testpack from the overview, with itsbaton.animation.jsonclip 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. -
In
geo, replacebaton.geo.jsonwith 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] } } } ] } ] } ] } -
In
definitions, replacebaton.jsonwith the text below. It keeps the idle clip from the overview and adds one emitter underparticles.{ "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] } ] } -
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
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.
- 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.
- Switch the stage to Particles mode with the header button or the 3 key.
- Choose Add > Add Emitter. The emitter appears under Particles in the outliner.
- In the properties, open the Particles tab. Set Type to
minecraft:flameand Bone totip. 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. - Set Chance to
0.4, Life to0.6, Size to0.04and End Size to0.01. - Choose View > Live Link with Punchy (Ctrl+L) and hold the item. Punchy! draws your unsaved emitter in your hand.
- 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.
- Choose File > Export Model Part… (Ctrl+E) to write the pack.
Video coming soon
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
trueandfalse. Set"enabled": falseto switch an emitter off without deleting it.triggersandon_animationare small objects with their own keys, explained below. Every key on this page works in an object except the long glow namesemissive_strength,emissive_blur,emissive_clampand theiremission_spellings, which are read in script strings only. Writestrength,blurandclampin an object. - Script string. Keys are
key=valuepairs separated by commas. A vector is three numbers joined with colons, as invelocity=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 excepttriggersandenabled, which are for objects only. In a script use thetrigger_keys and leaveenabledout. A string that starts withparticle(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
=becomesparticle(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_emissionandparticleEmission. Each holds one emitter or a list, and the lists are joined. - In a compat rule. The
itementries and theitemSpecificentries of a compat file readparticle_emittersfirst 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 byface). 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_variantsin anitemSpecificentry 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.
- 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. - 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.
- 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
countparticles. - 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.
- Each release rolls
chanceonce and then spawnscountparticles.
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
| Family | Keys | Kind | True or released when |
|---|---|---|---|
| Jump | trigger_, jump | Pulse | You take off from the ground. One burst per jump. |
| Landing | trigger_, landing | Pulse | You touch the ground. One burst per landing. |
| Attacking | trigger_, attacking, attack | Pulse | You swing or press attack with this hand. |
| Using | trigger_, use_item, useitem | Pulse | You use the item with this hand. |
| Clip moment | on_ with on_, or on_ | Pulse | The named item clip, or the arm clip of that name, passes the time you give. |
| Mining | trigger_, mining | Pulse and steady | You mine a block with your main hand. Releases a burst for each mining event, and the steady clock runs while you keep mining. |
| Walking | trigger_, walking | Steady | You are on the ground and moving or holding a movement key, and not sprinting or swimming. |
| Sprinting | trigger_, sprinting | Steady | You are sprinting and moving or holding a movement key. |
| Swimming | trigger_, swimming | Steady | You are swimming or in water. |
| Falling | trigger_, falling | Steady | You are in the air and dropping. trigger_ sets how far you must have fallen. |
| Biomes | trigger_, biomes, biome | Steady | The 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(alsoburst) is how many particles one release spawns, from 0 to 120.rate(alsorate_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(alsoemit_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_jumpwithcount: 8throws 8 particles per jump. - A stream is a steady emitter with a
rateoremit_interval. - A pulse-only emitter, one whose triggers are only pulses, ignores
rateandemit_interval. It releases once per event. chanceis rolled once for each release, not for each particle. At0.3three releases in ten spawn their wholecount.emit_for(alsoduration) 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) andease(linear,ease_in_quad,ease_out_quadorease_in_out_cubic) scalecountand the starting velocity. Acountof 10 withtof 0.5 and the linear ease spawns 5.easealso shapes how the size and alpha move over a particle’s life. Any othereasevalue plays aslinear.
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 }
}
]
}
- Flame. No triggers, so it runs on the steady clock all the time.
- Dust. Two pulses joined with
trigger_mode: any, so it fires on takeoff and on landing. - Sparks. A single pulse. Each swing throws 8.
- Mining sparks. The target
world_block_faceputs the particles on the face of the block you are mining.trigger_blocklimits it to blocks of that tag, andemit_intervalmakes a stream while you keep mining. Read the mined block below. - Puff. The clip
baton_swaylasts 2 seconds, so this fires halfway through each loop.
Video coming soon
The mined block
Three groups of keys tie an emitter to the block you are mining.
- Target.
target(alsoemit_target,target_type,emit_target_type) isboneby default, so particles start at a bone. Set it toworld_block_face(alsoworld_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(alsoblock_face,emit_face,mining_face) picks which face:hit(the face you hit, the default),random,opposite,up,down,north,south,westoreast.face_region(alsoface_bounds,surface,surface_region,surface_bounds) limits where on that face particles start:hitorexactfor the point you hit, or four numbersu:v:u:vfrom 0 to 1. Without it, particles start at the point you hit (the same ashit) 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(alsotrigger_blocks,block,blocks,mining_block,mining_blocks) is a list of block ids or#tagsseparated by|or;.trigger_block_state(alsoblock_state,mining_block_state) lists required state values such aswaterlogged=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(alsofull_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": falsefor a dull ash or smoke that follows the world’s light. Types withleaforleavesin the name start with fullbright and glow off.full_day_light(alsofulldaylight,day_fullbright,daylight_fullbright) makes the particle fully lit only in the overworld during the day. Write"fullbright": falseas well, or the default full light also stays on at night. It needs Enable Model Parts Glow.bright_at_dark(alsoblur_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 itsalphaand its glow it shows. It turnsfullbrightoff unless you also writefullbright. It shows only while Enable Blur Glow At Dark is on, and it does not need Enable Model Parts Glow.light_percent(alsolight_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, withfullbright, full light. When you write it, it replaces the full light offullbright, and anything below 100 also turns the glow layer off.
The glow layer is tuned with three more keys:
strength(alsoemissive,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, withblur, a halo grows around it up to 100.blur(alsoemissive_blur,emission_blur) adds that soft halo. It is off by default.clamp(alsoemissive_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
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:
- the id with
.pngadded textures/in front of the idtextures/particle/in front of the idtextures/in front of the id, when the id starts withparticle/
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:
- A particle definition
assets/<namespace>/particles/<name>.jsonin the pack or the game. The first picture in itstextureslist is used. - The picture
textures/particle/<name>.png, or<name>_0.png. A_particleending on the name and aparticle/start on it are dropped when the full name finds nothing. - By the name:
soulgivessoul_0,smokegivesgeneric_0, andcampfireorashgivesash. - 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.
- Make the pictures
spark_0.png,spark_1.png,spark_2.png,spark_3.pngand so on inassets/mypack/textures/particle/. Number them without gaps. Punchy! counts up from_1until 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. - Add the emitter below to the
particleslist inbaton.json, with"texture": "mypack:particle/spark_0". The id must end in_0. - 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
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
| Key | Type | Default | What it does | Example |
|---|---|---|---|---|
type | particle id | minecraft: | The 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: |
bone | text | none | The 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_ | text | bone | Where the particle starts. bone starts it at the bone. world_ 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_ (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_ moves the start away from the bone. | "world_ |
offset | vector | 0:0:0 | Moves the start from the bone, in blocks, in the space of offset_. | [0, 0.0625, 0] |
spread | vector | 0:0:0 | A 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] |
velocity | vector | 0:0:0 | The starting push, in the space of velocity_. It is scaled by t and ease like count. | [0, 0.01, 0] |
How many and how often
| Key | Type | Default | What it does | Example |
|---|---|---|---|---|
count, burst | whole number | 1 | Particles per release, 0 to 120, after t and ease scale it. | 6 |
chance | number | 1 | The chance, 0 to 1, that a release spawns anything. Rolled once per release. | 0.4 |
rate, rate_ | number | 0 | Releases per second for a steady emitter, 0 to 120. At most 24 releases per frame. | 6 |
emit_ | seconds | 0 | Seconds between releases for a steady emitter, 0 to 60. Wins over rate when above 0. | 0.12 |
emit_ | seconds | 0 | The emitter stops for good this many seconds after it started, 0 to 30. 0 means no limit. | 5 |
t | number | 1 | A share, 0 to 1, put through ease and multiplied into count and velocity. 0 spawns nothing. | 0.5 |
ease | text | linear | linear, ease_, ease_ or ease_. Shapes the effect of t, and how size and alpha change over a particle's life. Any other value plays as linear. | "ease_ |
Triggers
| Key | Type | Default | What it does | Example |
|---|---|---|---|---|
trigger_ | true or false | false | A pulse at takeoff. | |
trigger_ | true or false | false | A pulse on landing. | |
trigger_ | true or false | false | A pulse for each swing or attack of this hand. | |
trigger_ | true or false | false | A pulse each time this hand uses the item. | |
trigger_ | true or false | false | A pulse for each mining event, and the steady clock runs while you mine. Main hand only. | |
trigger_ | true or false | false | Steady. On the ground, moving, not sprinting and not swimming. | |
trigger_ | true or false | false | Steady. Sprinting and moving. | |
trigger_ | true or false | false | Steady. Swimming or in water. | |
trigger_ | true or false | false | Steady. In the air and dropping. | |
trigger_ | number | 0 | The shortest fall, in blocks, from 0 to 512, before falling counts. | 3 |
on_ | text | none | The item clip to watch, matched without regard to case. It needs a timecode to take effect. | "baton_ |
on_ | seconds | unset | The time in that clip at which one burst is released. Use a time above 0. | 1.0 |
on_ | text | Name and time in one value, name@seconds. In an object it can also be { "name": ..., "timecode": ... }. | "baton_ | |
trigger_ | list | none | Biome ids or #tags separated by | or ;. True when the biome at your feet is in the list. | "minecraft: |
trigger_ | text | all | any or or fires when one trigger is true. Anything else needs all of them. | "any" |
triggers | object | Object form only. The trigger flags as jump, landing, walking, sprinting, swimming, attacking, mining, falling (a flag, or { "min_) and biome. | ||
enabled | true or false | true | Object form only. false switches the emitter off. |
Look
| Key | Type | Default | What it does | Example |
|---|---|---|---|---|
life, lifetime | seconds | 1.1 | How long a particle lives, 0.05 to 60. | 0.6 |
size | number | 0.028 | Half the width of the square at the start, 0.001 to 2. Double it for a particle twice as wide. | 0.04 |
size_end | number | 0.006 | Half the width at the end of life, 0 to 2. | 0.01 |
alpha | number | 0.95 | Opacity at the start, 0 to 1. A number above 1 is read as 0 to 255. | 0.8 |
alpha_end | number | 0 | Opacity at the end of life. Same range as alpha. | 0 |
drag | number | 0.93 | The share of speed kept each tick, 0 to 1.2. | 0.9 |
gravity | number | 0 | Added to the vertical speed over time, -2 to 2. | 0.05 |
tint, color, rgb | vector | white | Red, green and blue, each 0 to 1, or each 0 to 255 when any is above 1. | [255, 200, 80] |
texture | resource id | the type's picture | The picture to draw. See Textures. A name ending in _0 plays as an animation. | "mypack: |
Light and glow
| Key | Type | Default | What it does | Example |
|---|---|---|---|---|
fullbright, full_ | true or false | true | Full 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_ | true or false | false | Full light only in the overworld by day. Needs Enable Model Parts Glow. | |
bright_ | true or false | false | Fades 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. | |
additive | true or false | false | The 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_ is drawn whole over the item. | true |
glow_ | true or false | false | Hides the particle whenever Enable Blur Glow At Dark is off. | |
light_ | number | world light | Mixes the world's light toward full light, 0 to 100. Below 100 the glow layer is not drawn. | 100 |
strength, emissive, emissive_ | whole number | 100 | The 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_ | true or false | false | Adds a soft halo. Use blur in an object. | |
clamp, emissive_ | true or false | true | Clamps the glow layer's picture at the edges. Use clamp in an object. |
Space and motion
| Key | Type | Default | What it does | Example |
|---|---|---|---|---|
offset_ | text | bone | The space offset is read in: bone, world, camera, block or block_face. face and blockface mean block_face. | |
velocity_ | text | world | The space velocity is read in. The same values as offset_. | |
ignore_ | true or false | true | The 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_ | text | linear | linear (also default, drag) or orbit (also orbital, circle). | |
motion_ | text | bone | The space the orbit axis and center are read in. | |
orbit_ | number | 0 | The orbit's radius in blocks, 0 to 4. | 0.06 |
orbit_ | number | 0 | Turns per second, -20 to 20. Negative turns the other way. | 1.5 |
orbit_ | vector | 0:1:0 | The axis the particle circles. | [0, 1, 0] |
orbit_ | number | 0 | The starting angle in degrees. The particles of one release are spread evenly from it. | 90 |
orbit_ | vector | 0:0:0 | Moves the orbit's center, in motion_. | [0, 0.1, 0] |
lock_ | true or false | false | Pins 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
| Key | Type | Default | What it does | Example |
|---|---|---|---|---|
face, block_ | text | hit | The face for a world_ emitter: hit (also impact, clicked), random, opposite (also back, reverse), up, down, north, south, west or east. | |
face_ | text | hit | Where 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. |
trigger_ | list | none | Block ids or #tags separated by | or ;. The mined block must be one of them. | "minecraft: |
trigger_ | list | none | Required 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
bonename 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": falseremoves one. Acountof 0, achanceof 0 or atof 0 spawns nothing. - A particle appears 20 times a second. An emitter with no triggers,
rateoremit_intervalreleases once per game tick. Addchance, arateor anemit_interval. - A pulse emitter ignores my rate. An emitter whose triggers are only pulses fires once per event and does not use
rateoremit_interval. Add a steady trigger and settrigger_modetoany, and the stream runs while that trigger holds. Or usemining, which is both. In the default mode a pulse and a steady trigger must both be true at once, and some pairs never are:jumpneeds you off the ground andwalkingneeds you on it. - An emitter with
jumpandlandingnever fires. The default mode needs both together. Add"trigger_mode": "any". - My triggers are ignored in a definition. Write
triggersas an object with named flags, and puttrigger_modebeside 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
triggersobject, atriggersobject whose flags are allfalse, or anon_animationwith a name and notimecodesets no trigger, so the emitter runs on the steady clock. In a compat rule, atriggerslist whose names are all misspelled does the same, because the list setsanymode 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_facetarget 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_lightorbright_at_darkis on, and thatlight_percentis unset or 100. Abright_at_darkparticle 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
.pngadded 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 intextureends 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
rateis capped at 120 a second.
Related pages
- Model parts overview builds the baton used on this page.
- Model part definitions covers the items a part matches and how it sits in the hand.
- Geometry and textures makes the bones the emitters sit on, and the glow of the model itself.
- Item clips plays the clips that
on_animationwatches. - Dynamic textures and Conditions react to what the item holds and to the world.
- Variants and presets ships several versions of one part.
- Compat rules puts emitters on items and on enchantments.
- Flags reference lists the keys of
shading.json. - Settings and controls has the particle and glow switches.
- Punchy! Studio for Punchy! packs covers the Model workspace.
Last checked against the mod on 29 October 2026.