Guides
Create a custom effect
Build your own effects with createEffect(), from a single burst to a toggleable fountain.
confetti() and sparkles() are built on the same function you can use yourself: createEffect(). This guide builds a custom effect step by step, from a few dots at the cursor position to a fountain of particles that can be toggled on and off. It assumes you know the parts of an effect, and how samplers produce values for each particle.
The smallest effect
Section titled “The smallest effect”An effect needs to know where it happens, when to emit particles, and what they look like. Everything else is optional:
Live Demo
import { cone, createEffect, range } from "party-js";
createEffect({ origin: { x: event.pageX, y: event.pageY }, schedule: { bursts: [{ count: 20 }] }, particleInit: { velocity: cone(90, 180, range(100, 300)), size: 8, color: "#ff5a5f", shape: { type: "circle" }, lifetime: 1, },});originis where the effect happens, here the point you clicked. On the default fullscreen canvas, it’s in page coordinates, the same onesevent.pageXandevent.pageYuse.scheduleemits a single burst of 20 particles, right when the effect starts.particleInitdescribes each particle as it spawns.size,color,shapeandlifetimeare required. Here, the particles also get a velocity:cone()points them upwards, with a deviation of 180 degrees to either side, so in every direction.
The position, orientation and spin of a particle are optional. Without them, particles start at the origin, face the viewer, and don’t spin.
Bursting out of an element
Section titled “Bursting out of an element”Most effects belong to an element, like a button that was clicked. measureTarget() measures an element and returns two things: its center as the origin, and its area to emit from. Passed to emitFrom(), the area spreads the particles over the whole element.
From there, a few more samplers and behaviors make the particles tumble and fall:
Live Demo
import { cone, createEffect, emitFrom, fade, gravity, measureTarget, pick, randomOrientation, randomSpin, range, scale } from "party-js";
const { origin, area } = measureTarget(button);
createEffect({ origin, schedule: { bursts: [{ count: 30 }] }, particleInit: { position: emitFrom(area), velocity: cone(90, 30, range(300, 600)), orientation: randomOrientation(), angularVelocity: randomSpin(90, 360), size: range(8, 12), color: pick(["#7dd3fc", "#38bdf8", "#0ea5e9"]), shape: { type: "square", aspectRatio: 0.5 }, lifetime: range(1.5, 2.5), }, behaviors: [gravity(800), scale(0.1, 0), fade(0, 0.5)],});randomOrientation()andrandomSpin()turn each particle in a random direction, and spin it around a random axis. That’s what makes the flat shapes look three-dimensional.gravity()pulls the particles down,scale()grows them in quickly, andfade()fades them out at the end of their life.
measureTarget() measures the element once, when it’s called. If the page scrolls afterwards, the particles stay where they were on the page, like the built-in effects do.
From bursts to streams
Section titled “From bursts to streams”A burst emits all of its particles at once. A rate emits them continuously instead, a number of particles per second. Together with loops: Infinity, the schedule never ends, so the effect runs until you end it:
Live Demo
import { createEffect, measureTarget } from "party-js";
let fountain;
// the first click starts the fountain, the second one turns it off and lets the last drops fallif (fountain) { fountain.end(); fountain = undefined;} else { const { origin, area } = measureTarget(button);
fountain = createEffect({ origin, schedule: { rate: 40, loops: Infinity }, // ... });}createEffect() returns a handle to the running effect, the same one the built-in effects return. Its end() method stops the fountain from emitting, and lets the particles that are already in the air finish their flight. Once the last one is gone, the effect ends by itself. To remove the particles right away instead, use stop().
The schedule can do more: several bursts at different times, repeated loops, or a rate that changes while the effect runs.
Making it reusable
Section titled “Making it reusable”To use your effect like a built-in one, wrap it in a function that takes a target and options, and returns the effect:
import { createEffect, measureTarget, emitFrom, pick } from "party-js";
export function fountain(target, options = {}) { const { rate = 40, colors = ["#7dd3fc", "#38bdf8", "#0ea5e9"] } = options; const { origin, area } = measureTarget(target);
return createEffect({ origin, schedule: { rate, loops: Infinity }, particleInit: { position: emitFrom(area), color: pick(colors), // ... }, // ... });}
const effect = fountain(button, { rate: 60 });measureTarget() accepts an element or a point, just like confetti(), so fountain() does too. Returning the effect lets the caller pause it, stop it, or wait for it to end with its finished promise.
Following an element
Section titled “Following an element”The origin can also be a function. party.js calls it again on every frame, so the effect can follow an element that moves:
createEffect({ origin: () => measureTarget(badge).origin, // ...});All particles are positioned relative to the origin, so when it moves, the whole effect moves with it, including the particles already in the air. That fits effects that belong to an element, like sparkles around a badge that slides in. It doesn’t leave a trail behind a moving element.
Measuring an element on every frame isn’t free, so use this for effects that need it, not for every burst.
Drawing on your own canvas
Section titled “Drawing on your own canvas”By default, effects draw on a fullscreen canvas that party.js manages for you. To draw somewhere else, for example behind your content, pass your own canvas. Its coordinates start at its top-left corner, so pass the canvas to measureTarget() as well, and it converts the origin for you:
const canvas = document.querySelector("#background");const { origin, area } = measureTarget(button, canvas);
createEffect({ canvas, origin, particleInit: { position: emitFrom(area), // ... }, // ...});Things to keep in mind
Section titled “Things to keep in mind”- Effects need a browser.
createEffect()throws aPartyJSErrorduring server-side rendering. Start effects from event handlers, or after your component has mounted. - Reduced motion is respected. When the user has asked their system for reduced motion, custom effects are skipped like the built-in ones. If your particles carry information the user would otherwise miss, pass
reducedMotion: "play". - Seeds make effects repeatable. Pass a
seed, and the effect looks the same every time. See Reproducible randomness.