Particle Configuration

Register named particle types with optional light, drift velocity, or expression-driven trajectories.

Particles are the small visual effects spawned by blocks, biomes, weather, and Lua scripts (sparks, smoke, leaves, dust). This is the catalog file that registers named particle types; each type’s texture is declared separately in your mod’s particle texture config.


File Location

Register the file in your mod’s manifest (mods/<YourMod>/<YourMod>.yaml):

particle-config: particle-config.yaml

Then create the file it points to, relative to your mod’s folder:

mods/<YourMod>/particle-config.yaml

The file is a single list of particle entries:

particles:
  - name: my_spark
    velocity:
      x: 0.0
      y: 5.0

Particle names are automatically prefixed with your mod name unless you write one already containing a colon, so my_spark becomes YourMod:my_spark — this is the full name used anywhere a particle is referenced (biome ambient particles, block particles, weather, Lua).


Minimal Example

particles:
  - name: my_dust
    velocity:
      x: 0.0
      y: 2.0

Fields

FieldDefaultDescription
name(required)The particle’s registered name.
light(none)Makes the particle emit light — same shape as Shared Blocks → light. Leave is-static at its false default so the light travels with the particle. Presence of light makes this a smart particle (see below).
velocity{x: 0.0, y: 0.0}Constant drift velocity, in blocks per second (+x is right, +y is up). Each axis is optional and defaults to 0, so a particle that only drifts one way can write just one (e.g. velocity: { y: 5.0 }). Honored by both regular and smart particles — for a smart particle it is a base drift added on top of any x-expression/y-expression offsets.
x-expression(none)An expression controlling horizontal position over time. Presence makes this a smart particle.
y-expression(none)An expression controlling vertical position over time. Presence makes this a smart particle.
predefined-variables(none)Extra named variables available to x-expression/y-expression — see Predefined Variables below.

Emitting particles: the particles: block

This file defines particles. Emitting them is done from whatever should produce them — a block, a tree, an interactable, or a biome — via a particles: key. That key has one shape everywhere: a list of emitter groups.

particles:
  - emit-random:
      - name: my_particle     # a particle defined in this file
        chance: 0.1
        cooldown-ms: 1000

Each list entry is one group: an emit-random list of emitters, plus whatever filters say when that group applies. Splitting emitters into several groups lets each one carry different filters. Which filters are available depends on what owns the block, because each owner answers a different question about where particles appear:

OwnerExtra keys on a groupWhy
Blockonly-emit-when-exposed-belowThe block is its own location; the only question left is whether it is covered.
Tree / Interactable(none)The object is its own location, with no exposure gate.
Biomevalid-blocks, block-spawn-source, min-altitude, max-altitudeA biome is a region, not a spot — each group has to say which blocks and which altitudes it applies to.

A group with an unrecognized key is skipped, and its siblings still load. The emit-random entries themselves take the same fields everywhere — see Emit Random for the full list.

Because a block’s gate is per group, one block can mix an always-on emitter with a gated one:

particles:
  - emit-random:                          # always emits
      - name: embers
  - only-emit-when-exposed-below: true     # only while nothing is directly beneath
    emit-random:
      - name: drips

Regular vs. Smart Particles

A particle becomes a smart particle if it declares light, x-expression, or y-expression — smart particles are positioned every frame by evaluating their expressions, and can carry a light source that travels with them. A particle with none of those three is a regular particle.

Both kinds honor velocity as a constant drift. For a smart particle, velocity is a base drift added on top of the x-expression/y-expression offsets — so a light-only particle (no position functions) can still drift by setting velocity, and its light follows along.

How long a spawned particle lives is not set here — it’s controlled by whatever spawns the particle. Block, tree, biome, and static-object ambient emitters each set a per-emitter lifetime-ms (and has-lifetime) on their emit-random entries, so the same particle type can live a different duration in different contexts.


Expressions

x-expression/y-expression/a variable’s value are all runtime math expressions — no Lua required for simple procedural motion. Supported syntax:

  • Arithmetic: +, -, *, /, mod
  • Trigonometry: sin, cos, tan
  • Power: pow(a, b)
  • Utility: randfloat(min, max), clamp(val, min, max)
  • Constants and variable names

The built-in variable total_time_s (seconds since the particle spawned) is always available. Add your own via predefined-variables.

x-expression/y-expression evaluate to the particle’s position offset from its spawn point, in blocks (+x right, +y up) — so y-expression: "-0.5 * total_time_s" drifts the particle downward at half a block per second.

x-expression: "0.5 * sin(total_time_s * 0.7)"
y-expression: "-0.5 * total_time_s"

Predefined Variables

A map of variable name to an expression, each evaluated once, at spawn time, and then available by name inside x-expression/y-expression for that particle’s whole lifetime — useful for giving each spawned particle its own randomized amplitude, offset, or speed instead of every instance moving identically:

predefined-variables:
  amplitude: "randfloat(0.5, 1.5)"
  x_offset: "randfloat(-0.25, 0.25)"
x-expression: "(amplitude * (1.0 + (0.5 * sin(total_time_s * 0.7)))) + x_offset * total_time_s"

Variable names must be snake_case — a valid identifier is [a-zA-Z_][a-zA-Z0-9_]*. Do not use hyphens: a name like x-offset is parsed inside an expression as the subtraction x - offset (two undefined variables), not as a single variable, and will silently misbehave.

Each predefined variable is a self-contained constant, evaluated on its own at spawn — it cannot reference another predefined variable or total_time_s (only x-expression/y-expression see those). A variable whose expression references anything outside its own scope silently evaluates to 0. Use randfloat/clamp/arithmetic on literals here; save the time-varying math for x-expression/y-expression.


Complete Example

Based on real shipped particles:

particles:
  - name: torch_spark
    light:
      magnitude: 0.5
      color:
        r: 1.0
        g: 0.5
        b: 0.0
        a: 0.8
      drop-off-distance: 2.0
      is-static: false

  - name: torch_smoke
    velocity:
      x: 0.0
      y: 5.0

  - name: cherry_leaf
    predefined-variables:
      amplitude: "randfloat(0.5, 1.5)"
      x_offset: "randfloat(-0.25, 0.25)"
    x-expression: "(amplitude * (1.0 + (0.5 * sin(total_time_s * 0.7)))) + x_offset * total_time_s"
    y-expression: "-0.5 * total_time_s"