Skip to content

Particles

This guide explains how to spawn different types of particles.

If the particle you’re trying to spawn isn’t mentioned in this guide, then it most likely has no special behavior.

There are two ways to spawn particles. The first option is using the ParticleBuilder class, which is preferred over the spawnParticle() methods. It is reusable and offers improved readability and clarity. The builder also includes the method receivers(), which provides you with greater control over receivers.

An example of spawning 14 note particles in a 4x0.4x4 cuboid:

Particle.NOTE.builder()
.location(someLocation)
.offset(2, 0.2, 2)
.count(14)
.receivers(32, true)
.spawn();

The second way is using the spawnParticle() methods in World and Player classes:

count argument behavior Important

Section titled “count argument behavior ”

When spawning particles, the Minecraft client behaves differently based on the count argument:

  • If count = 0, a singular particle spawns and the client uses the provided location without modification. The offset values are multiplied by the extra argument and passed to the particle constructor. The way these values are used may vary between particle types.

  • If count > 0, the client spawns count number of particles. For each particle, it generates new offset values using a Gaussian (normal) distribution, multiplies them by the extra argument, and passes them to the particle constructor.

This type of particle has an initial velocity when spawned.

In the following example 8 FLAME particles are spawned in a 1x1x1 cube shape randomly. someLocation serves as its center. The extra argument is set to 0, so the particles don’t move.

Particle.FLAME.builder()
.location(someLocation)
.offset(0.5, 0.5, 0.5)
.count(8)
.extra(0)
.receivers(32, true)
.spawn();

Setting the count parameter to anything positive will yield a random direction for the velocity as described in count argument behavior.

An example of spawning 6 CRIT particles at a location, without offset, that will move in a random direction at a moderate speed:

Particle.CRIT.builder()
.location(someLocation)
.count(6)
.extra(0.6)
.receivers(32, true)
.spawn();

Crit particles going in random directions

To specify the velocity’s direction, set the count argument to 0 and use the offset arguments as the direction vector. See count argument behavior for more details.

An example of a repeating task spawning campfire smoke that slowly goes “up” (positive Y axis):

ParticleBuilder particleBuilder = Particle.CAMPFIRE_SIGNAL_SMOKE.builder()
.location(someLocation)
.offset(0, 1, 0)
.count(0)
.extra(0.1);
Bukkit.getScheduler().runTaskTimer(plugin,
() -> particleBuilder.receivers(32, true).spawn(),
0, 4);

Campfire signal smoke going up

We could also make the smoke go down if we wanted to:

ParticleBuilder particleBuilder = Particle.CAMPFIRE_SIGNAL_SMOKE.builder()
.location(someLocation)
.offset(0, -1, 0)
.count(0)
.extra(0.1);
Bukkit.getScheduler().runTaskTimer(plugin,
() -> particleBuilder.receivers(32, true).spawn(),
0, 4);
Show list
  • BLOCK
  • BUBBLE
  • BUBBLE_COLUMN_UP
  • BUBBLE_POP
  • CAMPFIRE_COSY_SMOKE
  • CAMPFIRE_SIGNAL_SMOKE
  • CLOUD
  • CRIT
  • DAMAGE_INDICATOR
  • DRAGON_BREATH
  • DUST
  • DUST_COLOR_TRANSITION
  • DUST_PLUME
  • ELECTRIC_SPARK
  • ENCHANTED_HIT
  • END_ROD
  • FIREWORK
  • FISHING
  • FLAME
  • FLASH
  • GLOW_SQUID_INK
  • ITEM
  • LARGE_SMOKE
  • POOF
  • REVERSE_PORTAL
  • SCRAPE
  • SCULK_CHARGE
  • SCULK_CHARGE_POP
  • SCULK_SOUL
  • SMALL_FLAME
  • SMOKE
  • SNEEZE
  • SNOWFLAKE
  • SOUL
  • SOUL_FIRE_FLAME
  • SPIT
  • SQUID_INK
  • TOTEM_OF_UNDYING
  • TRIAL_SPAWNER_DETECTION
  • TRIAL_SPAWNER_DETECTION_OMINOUS
  • WAX_OFF
  • WAX_ON
  • WHITE_SMOKE

These particles can be colored by passing a Color object as the data argument.

Example of spawning 10 potion effect particles in a 2x2x2 area with a slightly translucent orange color:

Particle.ENTITY_EFFECT.builder()
.location(someLocation)
.offset(1, 1, 1)
.count(10)
.data(Color.fromARGB(200, 255, 128, 0))
.receivers(32, true)
.spawn();

Colored potion effect particles

Vanilla uses the dust particle for redstone particles. They can have a custom color by passing Particle.DustOptions as data.

An example of creating a vertical line of blue dust particles, that are two times the regular size:

ParticleBuilder particleBuilder = Particle.DUST.builder()
.color(Color.BLUE, 2.0f);
// We can reuse the builder
for (double i = -1.0; i <= 1.0; i += 0.25) {
particleBuilder.location(someLocation.clone().add(0, i, 0)).receivers(32, true).spawn();
}

Blue dust particles in a vertical line

Dust transition particles work exactly like dust particles, but instead of having a static color, they transition their color from one to another. A Particle.DustTransition is used for specifying the transition.

An example where three dust transition particles spawn on the x-axis within a 1-block length:

Particle.DUST_COLOR_TRANSITION.builder()
.location(someLocation)
.offset(0.5, 0, 0)
.count(3)
.colorTransition(Color.RED, Color.BLUE)
.receivers(32, true)
.spawn();

Dust transition particles in a line

The note particles will use the offsetX argument in a custom function to determine the color, see Note particle color picker for more details. offsetY and offsetZ are ignored in this case.

Example:

Particle.NOTE.builder()
.location(someLocation)
.offset(0.4f, 0, 0)
.count(0)
.receivers(32, true)
.spawn();

Note particle

This tool allows you to pick a color for the note particle by adjusting the offsetX value. It only allows you to choose offsetX values between -1.0 and 1.0; values outside this range will repeat the color pattern.

Note particle

offsetX = 0

Trail particles require you to pass a Particle.Trail object as data.

An example where eight randomly offset trail particles travel towards a specified location (someLocation.clone().add(-4, 0, 4)) with a yellow color and a travel time of 40 ticks:

Particle.TRAIL.builder()
.location(someLocation)
.offset(1, 1, 1)
.count(8)
.data(new Particle.Trail(someLocation.clone().add(-4, 0, 4), Color.YELLOW, 40))
.receivers(32, true)
.spawn();

Yellow trail particles floating towards the right side of the screen

As the name implies, this type of particle converges to a single point (location), which in this case is the supplied location. Offset arguments are used to determine the relative spawn location of the particle. The particle will then travel from this relative location to the supplied location.

An example where an enchantment particle will spawn at someLocation.clone().add(-2, 0, 2) and travel to someLocation:

Particle.ENCHANT.builder()
.location(someLocation)
.offset(-2, 0, 2)
.count(0)
.receivers(32, true)
.spawn();

Enchant particle going towards the center of the screen

Show list
  • ENCHANT
  • NAUTILUS
  • OMINOUS_SPAWNING
  • PORTAL
  • VAULT_CONNECTION

To spawn particles that require BlockData, simply put BlockData as its data argument.

Example:

Particle.BLOCK_CRUMBLE.builder()
.location(someLocation)
.count(4)
.data(BlockType.GLOWSTONE.createBlockData())
.receivers(32, true)
.spawn();

To spawn particles that require an ItemStack, simply put an ItemStack as its data argument.

Example:

Particle.ITEM.builder()
.location(someLocation)
.count(4)
.data(ItemStack.of(Material.DIAMOND_PICKAXE))
.receivers(32, true)
.spawn();

The SCULK_CHARGE particle takes a float as its data argument. This is used as the particle’s “roll.” Or, more formally, the angle the particle displays at in radians.

Example of spawning a sculk charge particle at 45° that doesn’t move:

Particle.SCULK_CHARGE.builder()
.location(someLocation)
.data((float) Math.toRadians(45))
.extra(0)
.receivers(32, true)
.spawn();

Sculk charge particle at 45°

The SHRIEK particle takes an integer as its data argument. This is used to set the delay in ticks before the particle spawns.

It is completely up to your implementation when choosing to use data or a scheduler.

Example where a shriek particle will spawn after one second:

Particle.SHRIEK.builder()
.location(someLocation)
.data(20)
.receivers(32, true)
.spawn();

Shriek particle

Vibration particles require you to pass a Vibration object as data, where you can choose between a location (Vibration.Destination.BlockDestination) or an entity target (Vibration.Destination.EntityDestination). The constructor’s second argument is the travel time in ticks.

An example where a vibration particle will spawn at someLocation and travel to otherLocation in 40 ticks:

Particle.VIBRATION.builder()
.location(someLocation)
.data(new Vibration(new Vibration.Destination.BlockDestination(otherLocation), 40))
.receivers(32, true)
.spawn();

Vibration particle going to the left side of the screen

These particles will use offsetY as the particle’s y-axis velocity.

If you set offsetX AND offsetZ to 0, the particle will have almost no x or z-axis velocity, but will still have a y-axis velocity set by the offsetY argument. In both cases, offsetX and offsetZ are not used in the velocity vector.

Example of spawning a GLOW particle that moves up:

Particle.GLOW.builder()
.location(someLocation)
.count(0)
.offset(0, 2, 0)
.receivers(32, true)
.spawn();

Glow particle going up

Show list
  • EFFECT
  • ENTITY_EFFECT
  • GLOW
  • INFESTED
  • INSTANT_EFFECT
  • RAID_OMEN
  • TRIAL_OMEN
  • WITCH

These particles can be scaled with offsetX, while offsetY and offsetZ are ignored. This chapter does not include dust particles, those particles are covered in Dust particles and Dust transition particles chapters.

The SWEEP_ATTACK particle’s scale is calculated as 1.0 - offsetX * 0.5.

An example where two sweep attack particles will spawn at someLocation. First one with a scale of 1.0 and the second one with a scale of 2.0 right after:

ParticleBuilder sweepAttackParticleBuilder = Particle.SWEEP_ATTACK.builder()
.location(someLocation)
.count(0)
.receivers(32, true)
.spawn();
Bukkit.getScheduler().runTaskLater(plugin,
() -> sweepAttackParticleBuilder.offset(-2.0, 0, 0).spawn(), 10);

Sweep attack particle scale comparison

The EXPLOSION particle’s scale is calculated as 2.0 * (1.0 - offsetX * 0.5).

An example where two explosion particles will spawn at someLocation. First one with a scale of 1.0 and the second one with a scale of 4.0 right after:

ParticleBuilder explosionParticleBuilder = Particle.EXPLOSION.builder()
.location(someLocation)
.offset(1, 0, 0)
.count(0)
.receivers(32, true)
.spawn();
Bukkit.getScheduler().runTaskLater(plugin,
() -> explosionParticleBuilder.offset(-2.0, 0, 0).spawn(), 10);

Explosion particle scale comparison

This chapter covers particles that have unique behaviors when spawning.

The ANGRY_VILLAGER particle always spawns 0.5 higher (y-axis) than the supplied location.

The CLOUD and SNEEZE particles move towards the player’s y level, if they are within two blocks distance from the player’s location. When they reach the player’s y level, their vertical velocity will be greatly reduced.

If the player is moving vertically, the particles will attempt to match the player’s vertical velocity.

The SPLASH particle uses the offsetX and offsetZ arguments to determine the particle’s velocity vector, if two conditions are met:

  1. offsetY is 0
  2. Either offsetX or offsetZ are not 0

The DAMAGE_INDICATOR particle adds 1.0 to the provided offsetY.

The DUST_PILLAR particle uses offsetY for the y-axis velocity, while offsetX and offsetZ are ignored.

The DUST_PLUME particle adds 0.15 to the provided offsetY.

The FIREFLY particle uses offsetY as the particle’s initial y-axis velocity, however, there is 50% chance for the offsetY’s sign to be inverted. This means that the particle will either move up or down, with equal probability.