npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@zakkster/lite-vfx

v1.0.6

Published

Recipe-based visual effects manager with deterministic spawning, OKLCH color, and zero-GC SoA engine.

Downloads

676

Readme

@zakkster/lite-vfx

npm version npm bundle size npm downloads npm total downloads TypeScript License: MIT

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 gradientscolorFn receives a 0→1 progress value and returns perceptually beautiful colors
  • Blend mode caching — only updates globalCompositeOperation when 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-lerp

Quick 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