# konfeti 0.6.0: full documentation > Zero-dependency, strictly typed, highly customizable canvas confetti with emoji, text, image and spritesheet particles. Version 0.6.0, MIT license, runs in the browser (ES2022). Install with `npm install konfeti`. The everyday API is the shared `Konfeti` object: `Konfeti.fire(options?)` fires one burst (or a list of bursts) on a fullscreen canvas and returns a handle. `KonfetiFactory.create()` makes a dedicated instance (own canvas, defaults, particle budget), `createWorker()` from `konfeti/worker` renders off the main thread, and `konfeti/lite` is the smallest entry (paper only until shapes are registered with `registerShapes`, no presets). Every option is optional and typed; the API reference gives each option's unit, range and default. The guide comes first, then the API reference. Index of the separate pages: https://konfeti.mbattaloglu.com/llms.txt --- # konfeti Zero-dependency, strictly typed, highly customizable canvas confetti — with first-class **emoji, text, image and spritesheet** particles, composable physics and a typed plugin API. **English** · [Türkçe](https://konfeti.mbattaloglu.com/docs/tr.md) **npm** ```sh npm install konfeti ``` **pnpm** ```sh pnpm add konfeti ``` **yarn** ```sh yarn add konfeti ``` ```ts import { Konfeti } from "konfeti"; Konfeti.fire(); // classic confetti pop ``` ## API at a glance ```ts import { Konfeti, // the ready-made, shared instance: draws on a fullscreen overlay canvas KonfetiFactory, // builds extra instances on your own canvas (Web Worker: createWorker in konfeti/worker) KonfetiPresets, // ready-to-fire looks: SNOW, FIREWORKS, STARS … extendPreset, // a preset plus your own settings type FireOptions, // the type of every fire() setting, for autocomplete } from "konfeti"; // ── the shared instance: nothing to set up ── Konfeti.fire(); // one classic confetti pop, upward from just below the screen center Konfeti.fire({ particleCount: 120, spread: 90, origin: { x: 0.5, y: 0.3 } }); // your own settings Konfeti.fire(KonfetiPresets.SNOW); // a preset, as is Konfeti.fire(extendPreset(KonfetiPresets.SNOW, { particleCount: 60 })); // a preset, tweaked const burst = Konfeti.fire(); // every fire() returns a handle for that burst… burst.pause(); // …freeze just this burst burst.resume(); // …continue it await burst; // …or wait until its last particle is gone const trail = Konfeti.emit({ rate: 40, follow: "pointer" }); // a stream that follows the pointer… trail.stop(); // …until you stop it Konfeti.onClick(button, { particleCount: 30 }); // fire from the click position on every click Konfeti.pause(); // freeze everything (e.g. while an ad is not visible) Konfeti.resume(); // continue everything Konfeti.reset(); // remove every particle at once // ── your own instance: own canvas, own defaults, own particle limit ── const stage = KonfetiFactory.create(canvas, { maxParticles: 800 }); stage.fire(); // same API as Konfeti, drawn on `canvas` stage.destroy(); // stop, clear and remove its listeners when you are done // ── typed options: every setting autocompletes and is checked ── const options: FireOptions = { particleCount: 80, paper: { colors: ["#d6ff3f", "#ffffff"] } }; Konfeti.fire(options); ``` ## Contents - [API at a glance](#api-at-a-glance) - [Basics](#basics) - [The paper particle](#the-paper-particle) - [Shapes](#shapes) - [Physics](#physics) - [Emission](#emission) - [Continuous emitter](#continuous-emitter) - [Formations](#formations) - [Presets](#presets) - [Hooks](#hooks) - [Instances & canvases](#instances--canvases) - [Worker rendering](#worker-rendering) - [Playable ads & webviews](#playable-ads--webviews) - [Custom shapes & physics](#custom-shapes--physics) - [Bundle size & `konfeti/lite`](#bundle-size--konfetilite) - [Units & ranges](#units--ranges) - [AI assistants](#ai-assistants) ## Basics ```ts import { Konfeti } from "konfeti"; const burst = Konfeti.fire({ particleCount: 120, angle: 60, // 90 = up, 0 = right spread: 55, origin: { x: 0, y: 0.7 }, // normalized canvas position }); burst.pause(); burst.resume(); await burst; // resolves when every particle is gone ``` `origin` can also be an **element** (particles start from its center) or a **click event**: ```ts button.addEventListener("click", (event) => Konfeti.fire({ origin: event })); Konfeti.fire({ origin: document.querySelector("#buy")! }); ``` Every numeric option accepts a fixed value or a random **range**: `10`, `[8, 14]` or `{ min: 8, max: 14 }`. Every handle reports its random **seed**. Fire the same options with it to get the same burst again: the same particles, colors and launch values. On an instance created with `fixedTimestep: true` the replay is exact, motion included, on any display and at any frame rate: ```ts const seed = Konfeti.fire({ particleCount: 80, spread: 90 }).getSeed(); setTimeout(() => Konfeti.fire({ particleCount: 80, spread: 90, seed }), 1200); // the same burst again ``` ```ts const stage = KonfetiFactory.create(canvas, { fixedTimestep: true }); // 1/60 s steps: exact replays ``` ## The paper particle The default confetti piece is fully configurable through `paper`: ```ts Konfeti.fire({ paper: { form: ["rect", "circle", { value: "strip", weight: 2 }], // weighted mix of silhouettes width: [6, 10], height: [12, 18], cornerRadius: 2, // or { tl: 6, br: 6 } skew: [-15, 15], colors: ["#ff0a54", "#ffd000", { color: "#00c2ff", weight: 3 }], backColor: "auto", // darkened front color while flipped stroke: { color: "white", width: 0.5 }, gradient: { colors: ["gold", "orange"], angle: 45 }, flip: { axis: "both", frequency: [0.5, 1.5] }, wobble: { amplitude: [2, 8] }, fadeOut: { start: 0.6, easing: "easeInQuad" }, scaleOverLife: { to: 0.4 }, shine: 0.5, trail: { length: 12 }, // a fading streak behind each piece }, }); ``` Color themes: `KonfetiPalettes.PASTEL`, `GOLD`, `NEON`, `RAINBOW`, `WINTER`, `AUTUMN`, `OCEAN`, `CANDY`, `FOREST`, `MONOCHROME` and `CLASSIC` are ready-made color lists — `paper: { colors: KonfetiPalettes.GOLD }`. Forms: `rect`, `square`, `circle`, `strip`, `leaf`. Other style keys: `scale`, `colorMode`, `backShade`, `colorOverLife`, `opacity`, `fadeIn`, `rotation`, `rotationSpeed`, `tilt`, `shadow`, `blendMode`, `trail` (it works on every shape, e.g. `{ type: "star", trail: true }`). Every option is documented in the type definitions — hover it in your editor. ## Shapes Mix any shapes with weights. Style keys set in `paper` are the base for every shape; each entry can override them. ```ts Konfeti.fire({ paper: { colors: ["#ff0a54", "#ffd000"] }, shapes: [ { type: "paper", weight: 4 }, { type: "star", points: [5, 6], shine: 0.6 }, { type: "heart", colors: "#ff4d6d" }, { type: "emoji", emoji: ["🎉", "🥳", { value: "✨", weight: 3 }], size: [22, 32] }, { type: "text", text: ["YAY", "WOW"], fontWeight: 900 }, { type: "image", src: "/coin.png", size: [20, 30] }, { type: "spritesheet", src: "/coin-spin.png", frames: { cols: 8, rows: 1 }, fps: 14 }, ], }); ``` | Type | Specific options | | ------------- | ------------------------------------------------------------------ | | `paper` | paper geometry (see above) | | `star` | `size`, `points`, `innerRatio` | | `triangle` | `size` | | `polygon` | `size`, `sides` | | `heart` | `size` | | `ribbon` | `length`, `thickness`, `waves` | | `path` | `path` (SVG `d` or `Path2D`), `viewBox`, `size` | | `emoji` | `emoji`, `size` (font size), `fontFamily` | | `text` | `text`, `size` (font size), `fontFamily`, `fontWeight` | | `image` | `src` (URL or any canvas image source), `size` (width), `tint` | | `spritesheet` | `src`, `frames`, `fps`, `loop`, `randomStartFrame`, `size`, `tint` | Emoji and text are rasterized once and cached. Preload URL images with `loadImage(url)`. An `image` or `spritesheet` `src` that starts with ` trail.stop(), 3000); // stop emitting; particles already out finish their lives await trail; // resolves once the last one is gone ``` `trail.moveTo(target)` switches what it follows, `clear()` removes everything at once, and `pause()` / `resume()` freeze it. Worker instances (`konfeti/worker`) emit too; the pointer or element is tracked on the main thread. ## Formations Particles first form a text or an image, hold it, then burst apart: a "You win!" end card, a launch announcement, a logo reveal. Every shape can take part, and the rest of the burst options apply as usual once the shape breaks up. ```ts Konfeti.fire({ origin: { x: 0.5, y: 0.45 }, // the center of the shape formation: { text: "TEBRİKLER", font: "900 110px sans-serif", mode: "assemble", // fly in from beyond the edges; "appear" shows the shape at once assemble: 900, // ms until the shape is complete hold: 1200, // ms before it bursts apart spacing: 8, // px between particles: smaller is denser and uses more particles }, shapes: [{ type: "paper", weight: 3 }, { type: "star" }], }); ``` ```ts // an image: its opaque pixels become the shape, painted in the image's own colors Konfeti.fire({ formation: { image: '', mode: "appear", hold: 1500, }, startVelocity: [200, 500], // how fast the pieces fly outward when the shape breaks up }); ``` - The particle count follows from `spacing`; an explicit `particleCount` only caps it. When `maxParticles` caps it, the whole shape thins out evenly instead of losing a part. - `fit` (default `0.9`) scales the whole formation (shape, spacing and particle size) down until it fits the canvas, so a phone shows a smaller copy of the same picture. - `startVelocity` is the burst-apart speed, `[300, 700]` px/s unless you set it. The particles' `lifetime` starts when they are released, so the shape stays fully visible until then. - An image can be a URL, inline SVG markup or any drawable image; the burst waits until it has loaded. `imageColors: false` keeps the shapes' own colors. A cross-origin image needs CORS headers. - Worker instances form shapes too, with the fonts the worker can see; inline SVG is not supported there. Formations need a single burst (`emission: { mode: "burst" }`, the default) and are not available for `emit()`. ## Presets ```ts import { Konfeti, KonfetiPresets, extendPreset } from "konfeti"; Konfeti.fire(KonfetiPresets.FIREWORKS); Konfeti.fire(extendPreset(KonfetiPresets.SNOW, { emission: { mode: "stream", duration: 10000 } })); // your settings win ``` `BASIC`, `REALISTIC`, `CANNON`, `SIDE_SHOTS`, `SCHOOL_PRIDE`, `FIREWORKS`, `SNOW`, `STARS`, `EMOJI_RAIN`, `HEART_BURST`, plus presets that show off newer options: `SHOOTING_STARS` (trails), `MAGNET` (attractor), `FORCE_FIELD` (repulsor), `GOLDEN` (gold palette), `SPARKLER` and `FIREFLIES` (additive glow), `JACKPOT` (floor), `CONGRATS`, `SUCCESS` and `LEVEL_UP` (text formations; `LEVEL_UP` is choreographed with `delay`) and `LOGO_REVEAL` (image formation). Presets are plain, read-only options (`SIDE_SHOTS` and `SCHOOL_PRIDE` are two bursts). `extendPreset` merges your settings into every burst without changing the preset; `paper` and `physics` merge key by key. ## Hooks ```ts Konfeti.fire({ onStart: () => {}, onParticleSpawn: (p) => {}, onParticleUpdate: (p, dt) => {}, // hot path, may mutate p.x / p.vx … onParticleDeath: (p) => {}, onComplete: () => {}, }); ``` ## Instances & canvases `Konfeti` is a shared fullscreen instance. Use `KonfetiFactory.create()` for your own canvas, separate defaults or a separate particle budget: ```ts import { KonfetiFactory } from "konfeti"; const stage = KonfetiFactory.create(document.querySelector("canvas"), { maxParticles: 800, disableForReducedMotion: true, defaults: { paper: { colors: ["gold", "white"] } }, }); stage.fire({ particleCount: 80 }); const off = stage.onClick(document.querySelector("#like")!, { particleCount: 30 }); off(); // stop listening stage.reset(); stage.destroy(); // also removes its click listeners ``` `onClick` takes a third argument: `trigger: "pointerdown"` fires as soon as a finger or button goes down (it keeps working when the page cancels touch events), and `onFire` receives each click burst's handle: ```ts Konfeti.onClick( button, { particleCount: 30 }, { trigger: "pointerdown", onFire: (burst) => burst.then(() => console.log("done")), }, ); ``` `pause()` freezes every burst on its current frame and `resume()` continues without counting the paused time (hidden tabs pause on their own). Both exist on `Konfeti` and on every instance. The first instance on a page logs a one-line banner with the version and renderer to the console. Call `disableBanner()` before firing to keep the console silent (`konfeti/lite` never prints it). ## Worker rendering `createWorker()` from `konfeti/worker` moves simulation and drawing into a Web Worker through an `OffscreenCanvas`, so large bursts keep animating while the main thread is busy (heavy React renders, long tasks, scrolling): ```ts import { createWorker } from "konfeti/worker"; // its own entry: other bundles carry no worker code const stage = createWorker(document.querySelector("canvas"), { maxParticles: 3000 }); await stage.fire({ particleCount: 1500, shapes: [{ type: "paper" }, { type: "emoji", emoji: "🎉" }], }); stage.isWorker(); // false when the browser cannot render offscreen — it then runs on the main thread stage.destroy(); // terminates the worker ``` Pass `null` (or nothing) for the canvas to get a fullscreen overlay, like `Konfeti`. Options travel to the worker by `postMessage`, so they must be structured-cloneable. The types (`WorkerFireOptions`) enforce this: | Works in a worker | Main thread only | | ------------------------------------------------------ | ---------------------------------------------------- | | every paper, style, physics and emission option | hooks (`onStart`, `onParticleSpawn`, `onComplete` …) | | built-in shapes, emoji and text | custom `defineShape` / `definePhysics` modules | | `image` / `spritesheet` with a URL or an `ImageBitmap` | `HTMLImageElement`, `Path2D` and other DOM sources | | `origin` as a point, an element or a click event | — | Element and click origins are measured on the main thread before sending; the pointer or element that an emitter follows or an attractor pulls toward is tracked there too and sent over while the burst runs. Use `await` on the handle instead of `onComplete`. The worker script itself (~19.1 kB brotli) is loaded only when the first worker instance is created. With a bundler it is inlined as a `blob:` URL; the `