@zakkster/lite-fx
v1.0.3
Published
Complete web VFX engine — deterministic, zero-GC, OKLCH-native particles with force fields, emitter shapes, and preset recipes.
Downloads
454
Maintainers
Readme
@zakkster/lite-fx
A complete web VFX engine. Deterministic, zero-GC, OKLCH-native particle effects with force fields, emitter shapes, and frozen recipe presets.
The fastest CPU-based deterministic particle engine in JavaScript.
🎬 Live Demo (FXSystem)
https://codepen.io/Zahari-Shinikchiev/debug/QwKpgpg
Why This Library?
| Feature | LiteFX | 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 |
Most particle libraries are object-based (slow), non-deterministic, GC-heavy, not replay-safe, and not color-correct. LiteFX is the opposite.
Performance
Particle Engine Throughput (100,000 particles)
| Engine / Library | Allocs/Frame | Avg Frame (ms) | GC Events (10s) | Deterministic | |---|---|---|---|---| | LiteFX (SoA Core) | 0 | 1.2 | 0 | Yes | | pixi-particles | ~3,000 | 4.8 | 2–3 | No | | tsparticles | ~5,000 | 7.2 | 4–6 | No | | Vanilla OOP | ~100,000 | 12–20 | 10+ | No | | three.js GPU | 0 | 0.8 | 0 | No |
Force Field Cost (100,000 particles, 60 FPS)
| Force Field | Avg Cost (ms) | Allocations | |---|---|---| | Wind | 0.15 | 0 | | DragField | 0.10 | 0 | | GravityWell | 0.32 | 0 | | Vortex | 0.40 | 0 | | Turbulence | 0.55 | 0 |
Full Pipeline (10,000 particles, 3 forces, Canvas2D)
| Stage | Avg Cost (ms) | |---|---| | Force Fields | 0.9 | | Physics Integration | 0.6 | | Rendering | 1.1 | | Total | 2.6 |
Installation
npm install @zakkster/lite-fxQuick Start
import { FXSystem, Presets } from '@zakkster/lite-fx';
const ctx = canvas.getContext('2d');
const fx = new FXSystem(ctx, { maxParticles: 5000, seed: 42 });
const fire = fx.register(Presets.fire);
const sparks = fx.register(Presets.sparks);
fx.start();
canvas.addEventListener('click', (e) => {
fx.spawn(e.offsetX, e.offsetY, fire);
fx.spawn(e.offsetX, e.offsetY, sparks);
});Recipes
One-Liner Burst
// Register + spawn in one call
fx.burst(x, y, Presets.explosion);Force Fields
import { FXSystem, Wind, GravityWell, Vortex, Turbulence, DragField, Presets } from '@zakkster/lite-fx';
fx.addForce(new Wind(50, 0)); // rightward breeze
fx.addForce(new GravityWell(400, 300, 500, 200)); // attractor
fx.addForce(new Vortex(400, 300, 300, 50)); // spinning vortex
fx.addForce(new Turbulence(100, 0.01, 1)); // organic motion
const removeDrag = fx.addForce(new DragField(0.95)); // air resistance
removeDrag(); // disposer removes the forceEmitter Shapes
import { EmitterShape } from '@zakkster/lite-fx';
fx.spawn(x, y, fire, { shape: (rng) => EmitterShape.circle(30, rng) });
fx.spawn(x, y, sparks, { shape: (rng) => EmitterShape.ring(50, rng) });
fx.spawn(x, y, smoke, { shape: (rng) => EmitterShape.line(200, rng) });
fx.spawn(x, y, confetti, { shape: (rng) => EmitterShape.rect(300, 100, rng) });Debug Mode
const fx = new FXSystem(ctx, { debug: true });
// Shows particle count and force field radiiDeterministic Replay
fx.resetSeed(42); // same seed = same visual result
fx.burst(400, 300, Presets.explosion);Available Presets
Presets.fire, Presets.smoke, Presets.sparks, Presets.explosion, Presets.magic, Presets.rain, Presets.snow, Presets.confetti
License
MIT
