Skip to main content

Particle Modules

Introduction#

Static particles can look pretty boring. Particle modules allow certain particle properties to change over time, for example rotation or color. These modules are represented as functions, that can be applied per-emitter on every particle in every tick.

export type ModuleFunction = (particle: Particle) => void;

Looks simple doesn't it? But don't be deceived - they are much more powerful than you might initially think!

Let's say you wanted to make your particle's sizes pulse over time using a sine wave. Well, then you just need to pass a function that does exactly that into your emitter's modules!

emitter.options.modules.push(
(particle: Particle) =>
(particle.size = Math.sin(new Date().getTime() / 1000))
);

However, writing functions like that for every single property you want to animate is boring and can get messy. You can still use them for more advanced cases, but for simple ones we will take a look at the ModuleBuilder class.

Module Builder#

The ModuleBuilder class is a utility class that allows us to construct ModuleFunctions (as seen above) from specific parameters. Let's jump in and take a look at an example:

new ModuleBuilder()
.drive("size")
.by((t) => Math.min(1, t * 3))
.through("lifetime")
.build(),

This can be read as: we're creating a new module builder that drives the size of a particle using the specified function, that takes the particle's lifetime as a factor.

.drive(key)#

Specifies the particle property to drive through the module function. Currently supported are color, opacity, rotation and size.

.by(factor)#

Specifies the value to drive the value by. This can either be a constant, a spline or a function that calculates a value from a factor and the particle as context.

type ModuleDriverValue<T> =
| T
| Spline<T>
| ((factor: number, particle?: Particle) => T);

.through(factorType)#

Specifies the factor used to evaluate the driving value specified with .by(). Currently supports lifetime (in seconds), relativeLifetime (from 0 to 1) and size. By default, this uses the lifetime factor.

.relative()#

Chains the module function to be applied relative to the initial value. Note that this is only supported for properties that have an initial value in the particle object, currently rotation and size. The operation applied to the new and initial value is inferred from their types:

  • Vector: Both vectors are added.
  • number: Both numbers are multiplied.

Example#

party.sparkles(mouseEvent, {
emitter: {
modules: [
new party.ModuleBuilder()
.drive("size")
.by(
new party.NumericSpline(
{ time: 0, value: 0 },
{ time: 0.5, value: 1 },
{ time: 1, value: 0 }
)
)
.build(),
new party.ModuleBuilder()
.drive("opacity")
.by((t) => t)
.through("size")
.build(),
],
},
});
You can use the following objects in your code: party, mouseEvent, codeblock, runButton.