@zakkster/lite-vfx
v1.0.6
Published
Recipe-based visual effects manager with deterministic spawning, OKLCH color, and zero-GC SoA engine.
Downloads
676
Maintainers
Readme
@zakkster/lite-vfx
Recipe-based visual effects manager built on @zakkster/lite-soa-particle-engine.
Declare your effects as frozen recipe objects. Spawn them anywhere. Deterministic, zero-GC, OKLCH color.
🎬 Live Demo (VFX)
http://codepen.io/Zahari-Shinikchiev/debug/xbEqLLZ
🎬 Live Demo (VFX) 2
https://cdpn.io/pen/debug/myrBbBM
Feature Comparison
| Feature | Lite VFX | pixi.js | GSAP | anime.js | |---|---|---|---|---| | Deterministic | Yes | No | No | No | | Zero-GC | Yes | No | No | No | | OKLCH Color | Yes | No | No | No | | SoA Memory Layout | Yes | No | No | No | | Replay-Safe | Yes | No | No | No | | Custom Physics | Yes | Limited | Limited | Limited | | Canvas2D Performance | Excellent | Good | Medium | Medium |
Why This Library?
- Recipe system — define effects once as frozen objects, spawn anywhere with one line
- Deterministic — same seed = same visual result. Ship replays, reproduce bugs, test VFX in CI.
- SoA engine — flat TypedArrays, zero allocations, CPU cache-friendly
- OKLCH gradients —
colorFnreceives a 0→1 progress value and returns perceptually beautiful colors - Blend mode caching — only updates
globalCompositeOperationwhen the blend mode actually changes - Shape support —
'rect'or'circle'per recipe
Installation
npm install @zakkster/lite-vfx @zakkster/lite-soa-particle-engine @zakkster/lite-color @zakkster/lite-lerpQuick Start
import { VFXManager } from '@zakkster/lite-vfx';
import { createGradient } from '@zakkster/lite-color';
import { easeOut } from '@zakkster/lite-lerp';
import { Random } from '@zakkster/lite-random';
const ctx = canvas.getContext('2d');
const vfx = new VFXManager(ctx, 5000);
const rng = new Random(12345);
// Define a recipe
const VFX_SPARK = Object.freeze({
id: 1,
count: [15, 25],
life: [0.2, 0.5],
speed: [200, 500],
angle: [0, Math.PI * 2],
gravity: 400,
friction: 0.90,
size: [3, 1],
colorFn: createGradient([
{ l: 1.0, c: 0.0, h: 60 }, // white
{ l: 0.9, c: 0.1, h: 50 }, // warm yellow
], easeOut),
blendMode: 'screen',
});
vfx.register(VFX_SPARK);
vfx.start();
// Spawn on click
canvas.addEventListener('click', (e) => {
vfx.spawn(e.offsetX, e.offsetY, VFX_SPARK, rng);
});Defining Recipes
A recipe is a plain object describing a particle effect. Freeze it for safety:
const VFX_FIRE = Object.freeze({
id: 1, // Unique integer ID
count: [5, 10], // Particles per burst [min, max]
life: [0.4, 0.8], // Lifetime in seconds [min, max]
speed: [50, 150], // Starting speed [min, max]
angle: [-Math.PI/2 - 0.5, -Math.PI/2 + 0.5], // Upward cone
gravity: -100, // Negative = floats up
friction: 0.95, // Per-frame velocity multiplier
size: [6, 0], // Shrinks from 6px to 0px [birth, death]
colorFn: createGradient([
{ l: 1.0, c: 0.0, h: 60 }, // white core
{ l: 0.8, c: 0.25, h: 40 }, // orange
{ l: 0.5, c: 0.30, h: 15 }, // red
{ l: 0.15, c: 0.05, h: 0 }, // dark smoke
], easeOut),
blendMode: 'screen',
shape: 'circle',
});
const VFX_SPARK = Object.freeze({
id: 2,
count: [15, 25],
life: [0.2, 0.5],
speed: [200, 500],
angle: [0, Math.PI * 2], // 360° burst
gravity: 400, // Heavy fall
friction: 0.90,
size: [3, 1],
colorFn: createGradient([
{ l: 1.0, c: 0.0, h: 60 },
{ l: 0.9, c: 0.15, h: 50 },
], easeOut),
blendMode: 'screen',
});Recipes Gallery
Fire + Sparks on Hit
function onHit(x, y) {
vfx.spawn(x, y, VFX_SPARK, rng);
vfx.spawn(x, y, VFX_FIRE, rng);
}Confetti Burst
const VFX_CONFETTI = Object.freeze({
id: 3,
count: [40, 60],
life: [1.5, 3.0],
speed: [100, 300],
angle: [-Math.PI/2 - 0.8, -Math.PI/2 + 0.8],
gravity: 200,
friction: 0.97,
size: [5, 3],
colorFn: (t) => {
// Cycle through rainbow based on progress
return { l: 0.7, c: 0.25, h: t * 360 };
},
shape: 'rect',
});Heal/Buff Sparkle
const VFX_HEAL = Object.freeze({
id: 4,
count: [8, 12],
life: [0.5, 1.0],
speed: [20, 60],
angle: [-Math.PI/2 - 0.3, -Math.PI/2 + 0.3],
gravity: -50,
friction: 0.98,
size: [4, 0],
colorFn: createGradient([
{ l: 0.95, c: 0.1, h: 150 }, // bright green
{ l: 0.6, c: 0.2, h: 130 }, // deep green
]),
blendMode: 'screen',
shape: 'circle',
});Deterministic Replay
Same seed, same visual result — every time:
const replaySeed = 42;
function playEffect(x, y) {
const rng = new Random(replaySeed); // same seed each time
vfx.spawn(x, y, VFX_SPARK, rng);
// Identical particles, positions, colors, every frame
}API
new VFXManager(ctx, maxParticles?)
| Param | Type | Default | Description |
|-------|------|---------|-------------|
| ctx | CanvasRenderingContext2D | required | Canvas context |
| maxParticles | number | 2000 | Shared pool for all recipes |
Methods
| Method | Description |
|--------|-------------|
| .register(recipe) | Register a recipe by its integer ID |
| .spawn(x, y, recipe, rng) | Spawn particles. rng needs .int() and .range() |
| .start() | Start the RAF render loop |
| .stop() | Pause rendering |
| .clear() | Kill all particles |
| .destroy() | Clean up everything. Idempotent. |
Recipe Shape
| Field | Type | Description |
|-------|------|-------------|
| id | number | Non-negative integer. Array index for O(1) lookup. |
| count | [min, max] | Particles per burst |
| life | [min, max] | Lifetime in seconds |
| speed | [min, max] | Starting speed (pixels/s) |
| angle | [min, max] | Emission angle (radians) |
| gravity | number | Acceleration (px/s²). Negative = floats up. |
| friction | number | Per-frame velocity multiplier (0–1) |
| size | [birth, death] | Pixel size interpolated over life |
| colorFn | (0→1) => OklchColor | Color function. Use createGradient(). |
| blendMode | string | Canvas composite operation. Default: 'source-over' |
| shape | string | 'rect' (default) or 'circle' |
The @zakkster Ecosystem
lite-lerp (math primitives)
└─ lite-color (OKLCH interpolation)
└─ lite-theme-gen (design system generation)
lite-random (deterministic RNG)
lite-object-pool (GC-free recycling)
└─ lite-particles (headless particle engine)
lite-soa-particle-engine (TypedArray particle engine)
└─ lite-vfx (recipe-based VFX manager)TypeScript
import { VFXManager, type VFXRecipe, type VFXRng } from '@zakkster/lite-vfx';License
MIT
