Skip to content

Particles

Lumii edited this page Nov 7, 2026 · 5 revisions

Particle Utilities

Citrine provides a configurable particle system with support for multiple behaviour parameters.

All particle utilities are located in:

org.carpentry.citrine.api.util.particles

Overview

Citrine's particle system consists of the following components:

Component Description
CitrineParticles Registers and provides built-in particle types.
ParticleBuilder Configures and spawns particles.
ParticleProperties Stores particle configuration and handles serialization.
CitrineParticleEffect Represents a configured particle effect.
CitrineParticle Handles particle rendering and behavior on the client.

Getting Started

Citrine provides several built-in particle types that can be used immediately.

CitrineParticles.GENERIC
CitrineParticles.GENERIC_ALT
CitrineParticles.SQUARE
CitrineParticles.DUST
CitrineParticles.CLOUD
CitrineParticles.STAR
CitrineParticles.TWINKLE

To spawn a particle, use ParticleBuilder:

ParticleBuilder.create(CitrineParticles.GENERIC)
    .lifetime(40)
    .scale(0.5f)
    .color(0xFF0000)
    .spawn(world, x, y, z);

This creates a particle with a lifetime of 40 ticks, a scale of 0.5, and a red color.


Particle Registration

CitrineParticles provides methods for registering particle types and contains references to Citrine's built-in particle types.

Methods

register(Identifier id)

Registers a new ParticleType<CitrineParticleEffect> using the provided identifier.

Returns the registered particle type, which can then be passed to ParticleBuilder.

Parameter Description
id The identifier used to register the particle type.

Registering Custom Particle Types

Mods can register their own particle types using register().

Currently, particles with animated/interchanging textures (such as end rod particle textures) are not supported by Citrine's particle system.

public static final ParticleType<CitrineParticleEffect> MY_PARTICLE =
    CitrineParticles.register(new Identifier("mymod", "my_particle"));

Particle Configuration

ParticleBuilder is the primary interface for configuring and spawning particles.

Creating a Builder

create(ParticleType<CitrineParticleEffect> type)

Creates a new ParticleBuilder for the specified particle type.

ParticleBuilder.create(CitrineParticles.GENERIC);

Each builder starts with the default particle configuration.

Properties

Lifetime

lifetime(int ticks)

Sets the particle's maximum lifetime in ticks.

20 ticks equal one second.

Default: 20

.lifetime(40)

In this example, the particle lasts approximately 2 seconds.

Scale

scale(float s)

Sets both the starting and ending scale to the same value.

scale(float start, float end)

Sets the starting and ending scale independently.

Parameter Description
start The particle's initial scale.
end The particle's final scale.

Default: 0.1f

.scale(0.1f, 1.0f)

The particle gradually grows from 0.1 to 1.0 over its lifetime.

Color

color(int rgb)

Sets both the starting and ending color to the same RGB value.

color(int start, int end)

Sets the starting and ending colors independently.

Parameter Description
start The particle's initial RGB color.
end The particle's final RGB color.

Colors use hex RGB values (0xRRGGBB).

Default: 0xFFFFFF

.color(0xFF0000, 0x0000FF)

Alpha

alpha(float a)

Sets both the starting and ending alpha values to the same value.

alpha(float start, float end)

Sets the starting and ending transparency independently.

Parameter Description
start The particle's initial alpha value.
end The particle's final alpha value.

Default: 1.0f

Values range from 0 (fully transparent) to 1 (fully opaque).

.alpha(1, 0)

The particle gradually fades out over its lifetime.

Rotation

rotation(float radians)

Sets the particle's initial rotation in radians.

Default: 0

.rotation(1f)

Spin

spin(float radiansPerTick)

Sets the amount of rotation applied to the particle every tick.

Default: 0.0f

.spin(0.1f)

Positive values rotate the particle in one direction, while negative values rotate it in the opposite direction.

Gravity

gravity(float g)

Sets the particle's gravity strength.

Default: 0.0f

.gravity(1.0f)

Higher values increase the effect of gravity, while negative values can produce upward acceleration.

Drag

drag(float multiplier)

Sets the particle's velocity multiplier.

Default: 0.98f

.drag(0.95f)

Values below 1.0f gradually reduce the particle's velocity, while values above 1.0f increase it.

Fullbright

fullBright()

Enables fullbright rendering for the particle.

When enabled, the particle ignores normal world lighting and renders at maximum brightness.

Default: false

.fullBright()

Collision

noCollision()

Disables collision with the world, the collision is enabled by default.

.noCollision()

Particle Motion

In addition to configuring the particle itself, ParticleBuilder provides methods for controlling its initial velocity.

Unlike the other properties, motion is not stored inside ParticleProperties. It is supplied separately when the particle is spawned.

Motion

motion(double x, double y, double z)

Sets the particle's initial velocity on each axis.

Parameter Description
x Initial horizontal velocity along the X axis.
y Initial vertical velocity along the Y axis.
z Initial horizontal velocity along the Z axis.

Default: 0.0 on all axes.

.motion(0.0, 0.5, 0.0)

This gives the particle an initial upward velocity.

Random Motion

randomMotion(double amount)

Adds randomized velocity to each axis when spawning particles.

The randomized value is independently calculated for X, Y, and Z within the range of -amount to +amount.

Default: 0.0

.randomMotion(0.2)

This adds a randomized velocity component to each particle, producing a scattered effect.

Combining Motion

Both methods can be used together.

.motion(0.0, 0.5, 0.0)
.randomMotion(0.2)

In this example, every particle receives an upward velocity of 0.5, with additional randomized movement on all three axes.


Building and Spawning

build()

Creates and returns a CitrineParticleEffect containing the configured particle properties.

This method does not spawn a particle.

CitrineParticleEffect effect = ParticleBuilder.create(CitrineParticles.GENERIC)
    .lifetime(40)
    .color(0xFF0000)
    .build();

The resulting effect can be used wherever a CitrineParticleEffect is required.

spawn(World world, double x, double y, double z)

Spawns a single particle at the specified position.

spawn(World world, double x, double y, double z, int count)

Spawns the specified number of particles at the given position.

Parameter Description
world The world in which the particles should spawn.
x X coordinate of the spawn position.
y Y coordinate of the spawn position.
z Z coordinate of the spawn position.
count Number of particles to spawn.

The spawning method automatically handles client and server worlds:

  • On a ServerWorld, particles are sent to clients using spawnParticles().
  • On a client world, particles are added locally using addParticle().

Each particle receives its own randomized velocity when random motion is configured.

Example

ParticleBuilder.create(CitrineParticles.GENERIC)
    .lifetime(40)
    .scale(0.5f, 1.0f)
    .color(0xFF0000, 0x0000FF)
    .alpha(1.0f, 0.0f)
    .motion(0.0, 0.5, 0.0)
    .randomMotion(0.2)
    .spawn(world, x, y, z, 16);

This spawns 16 particles at the specified position, each with randomized movement and a gradual color and transparency transition.


ParticleProperties

ParticleProperties is a record containing all configurable particle properties.

It is primarily used internally by Citrine's particle system, but can also be instantiated directly if needed.

Parameters

Parameter Type Description
lifetime int Maximum particle lifetime in ticks.
startScale float Initial particle scale.
endScale float Final particle scale.
startColor int Initial RGB color.
endColor int Final RGB color.
startAlpha float Initial transparency.
endAlpha float Final transparency.
rotation float Initial rotation in radians.
spin float Rotation applied per tick.
gravity float Gravity strength.
drag float Velocity multiplier.
fullBright boolean Whether full-bright rendering is enabled.
collision boolean Whether world collision is enabled.

DEFAULT

ParticleProperties.DEFAULT provides the default particle configuration.

Property Default
Lifetime 20
Start scale 0.1f
End scale 0.1f
Start color 0xFFFFFF
End color 0xFFFFFF
Start alpha 1.0f
End alpha 1.0f
Rotation 0.0f
Spin 0.0f
Gravity 0.0f
Drag 0.98f
Full bright false
Collision true

Serialization

ParticleProperties provides serialization methods for networking and data codecs.

write(PacketByteBuf buf)

Writes the particle properties into the provided packet buffer.

read(PacketByteBuf buf)

Reads particle properties from a packet buffer and returns a new ParticleProperties instance.

CODEC

Provides a Mojang Codec<ParticleProperties> for serialization and deserialization through Minecraft's codec system.

All properties are represented by named fields in the codec.


Particle Effects

CitrineParticleEffect

CitrineParticleEffect implements Minecraft's ParticleEffect interface.

It represents a particle effect containing:

  • A registered particle type.
  • A ParticleProperties instance.

Methods

props()

Returns the ParticleProperties associated with this effect.

getType()

Returns the registered particle type.

write(PacketByteBuf buf)

Serializes the particle properties into the provided packet buffer.

asString()

Returns the registered particle type's identifier as a string.

FACTORY

FACTORY provides the factory used to deserialize particle effects.

It supports reading from both a StringReader and a PacketByteBuf.

When reading from a StringReader, the factory uses ParticleProperties.DEFAULT. This means the vanilla /particle command uses default properties rather than custom builder configurations.

codec()

codec(ParticleType<CitrineParticleEffect> type)

Creates a codec for a particle effect associated with the specified particle type.

This codec converts between ParticleProperties and CitrineParticleEffect.


Custom Particle Rendering

CitrineParticle

CitrineParticle is Citrine's client-side particle implementation.

It extends Minecraft's SpriteBillboardParticle and handles the rendering and behavior of individual particles.

Its functionality includes:

  • Interpolating between starting and ending colors.
  • Interpolating between starting and ending alpha values.
  • Interpolating between starting and ending scales.
  • Applying rotation and spin.
  • Applying gravity and drag.
  • Handling world collision.
  • Supporting full-bright rendering.

Most developers will not need to interact with this class directly, as ParticleBuilder handles configuration and spawning.

Factory

CitrineParticle.Factory implements Minecraft's ParticleFactory<CitrineParticleEffect>.

It creates individual CitrineParticle instances using the supplied particle effect, position, velocity, and sprite provider.

Custom particle types must have an appropriate client-side factory registered to render correctly.


Complete Example

The following example demonstrates a particle effect using several of Citrine's available configuration options.

ParticleBuilder.create(CitrineParticles.GENERIC)
    .lifetime(TimeUtils.seconds(5))
    .scale(1.0f, 1.0f)
    .color(Color.CYAN.getRGB(), Color.BLUE.getRGB())
    .alpha(1.0f, 0.0f)
    .spin(0.5f)
    .gravity(1.0f)
    .motion(0.0, 0.5, 0.0)
    .randomMotion(0.2)
    .fullBright()
    .spawn(
        world,
        user.getPos().x,
        user.getPos().y + user.getHeight() / 2,
        user.getPos().z,
        16
    );

Result

This configuration creates 16 particles around the player's position.

Each particle:

  1. Has a lifetime of 5 seconds.
  2. Maintains a scale of 1.0.
  3. Transitions from cyan to blue.
  4. Gradually fades out.
  5. Rotates at 0.5 radians per tick.
  6. Is affected by gravity.
  7. Receives an initial upward velocity.
  8. Has randomized movement.
  9. Renders at full brightness.

Additional Notes

  • Particle configuration and spawn motion are separate. Motion is not stored in ParticleProperties.
  • Particle lifetimes are measured in ticks, with 20 ticks representing one second.
  • Color values use RGB channels. Alpha is configured separately.
  • Particle scale, color, and alpha are interpolated over the particle's lifetime.
  • fullBright() disables normal world lighting for the particle.
  • noCollision() disables collision with the world.
  • Particle factories and textures are client-side concerns. Registering a particle type alone does not provide a rendered particle.
  • When spawning particles on a server, use the server world so the effect can be distributed to connected clients.

For further information, refer to the individual implementations in Citrine's source code.

Clone this wiki locally