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

@adatechnology/game-vfx

v0.8.0

Published

Renderer-free visual effects, deterministic and testable in Node: the Explosion Lab's layer compositor (flash, fireball that cools into smoke, shock wave, dust, shrapnel, glowing debris, marks, mushroom, lingering fire) with its 16 profiles, seeded random

Readme

@adatechnology/game-vfx

Visual effects with no renderer, deterministic and testable in Node. It knows nothing about any game: the host gives it a camera position and a clock, calls update(dt), and reads plain pools (puffs, sparks, glows, chunks, shocks, rings, lights, marks) that a renderer draws (see @adatechnology/game-vfx-three).

It is a literal port of the Explosion Lab of the effects team: the layer compositor (blast) and the simulation loops, with the same draws from the same seeded stream in the same order, so the same seed gives the same explosion.

  • Layers of an explosion, in the Lab's order: flash and light, fireball that cools into smoke, the explosion's own smoke, shock wave (it pushes the smoke it meets), low dust, shrapnel and embers, glowing debris, then marks on the ground (scorch, crater with a dented ground field and rim rocks), mushroom cloud, lingering fires. Six layers can be turned off one by one (flash, fire, shock, smoke, shrapnel, mark).
  • Pools (OrderedPool): live records in the order they were added, reused instead of allocated, compacted in place. A full pool drops the oldest record (chunks drop a resting one first).
  • Random (lehmer, lcg, streamFor): randomMode: 'per_event' (default) gives each explosion a stream of its own seed, so a LAN client repeats the visual from a few bytes whatever else is playing; 'shared' uses one stream for everything, like the Lab page (the goldens).
  • LOD and cap: LOD_GAME (15 / 40 / 80 m, scales 1 / .5 / .2 / .1, default) and LOD_LAB; the game's area cap (the 7th explosion within 40 m in 4 s is made at 0.3, AREA_CAP_GAME).
  • Screen (screen()): shake, flash (the flash grenade blinds only who is near), and the Workshop's white, red, sides, chroma, desaturation, gas and beat, with the same decays.
  • Budgets: PROFILES (high bench and goldens, medium game default, low), EVENT_BUDGET (400 particles, 40 debris, 4 + 2 lights) and estimateCost(def) (the number of the Lab's effect sheet).
  • Side requests (onRequest): what an effect cannot do alone. crater (dig a ground you own), blast_front (glass, characters, with speed 340 m/s so the host delays by distance) and ignite (characters inside the radius catch fire).
import { createGroundField, createVfx, LOD_LAB } from '@adatechnology/game-vfx'

const vfx = createVfx({
  seed: 42,
  profile: 'high',
  lod: LOD_LAB,
  ground: createGroundField(), // optional: craters dig it, bouncing things read it
  explosions: myExplosions, // Record<string, ExplosionDef>; the 16 of the Lab arrive with the catalogue
  onRequest: (request) => host.handle(request),
})
vfx.setCamera([10, 6.5, 14])
vfx.explosion('m67', [0, 0.1, 0], { seed: 7 }) // or { direction: [1, 0, 0], scale: 2, delay: 0.5 }
vfx.fire({ point: [2, 0, 0], radius: 1.4, life: 7 })

// every frame
vfx.update(dt)
render(vfx.pools(), vfx.screen())

explosion answers a handle (or null for an unknown id), stop(handle) ends a fire, clear() takes everything away and restarts the clock, stats() reports what is alive, what the last explosion made, the scale it was made at and the seconds since.

Workshop recipes, sprites and event effects

vfx.play(id, point, { direction?, normal?, seed?, param? }) plays one of the 39 recipes of the Workshop of VFX (the 36 of the first pass and the weather ones: rain_and_puddles three intensities, fog_and_ash, headlights_in_rain; the rain lives in workshop().rain, a RainField that game-weather-three also drives) (blood by infection age, headshot, dismemberment, bloater cloud, scream, muzzle flashes by weapon, impact by material, glass, doors, collapses, fires, cars, the screen effects) into the Workshop pools (vfx.workshop(): additive and normal sprites, streaks, physics debris, decals, rings, flash and fire lights, screen state). The recipes are data (WORKSHOP_EFFECTS, EffectDef: a small language of expressions and steps) that a runner executes in the page's own order of random draws, so the goldens (tests/workshop-golden.test.ts, 162 assertions over 54 runs) replay the recording of the page. param picks the variant of a recipe by index or id ('fresh', 'ak', 'concrete'); anchor and heading of the recipe place and turn it at point and direction; seed makes it repeatable on a LAN client. patchEffects(base, changes) tunes, adds or removes recipes like patchExplosions does.

The Workshop also keeps solids, not only sprites: the mesh step spawns a MeshSpawn (cone, open cone, sphere or icosahedron with its segments, additive or lit material, a place and a turn inside a group, a scale ramp, growth, drag, gravity, spin and a fading opacity) into workshop().meshes (MeshPool), aged each frame in the order of the gallery's Particles. play can also take the thing's whole turn (orientation, a unit quaternion, pitch and roll included, instead of the turn about the vertical axis direction gives) and named numbers (values, read with variable(name)). VIEWMODEL_EFFECTS use all three: the first-person muzzle flash (viewmodel_muzzle_flash: an open cone, four small cones and a core for 55 ms, a light that holds, six spheres of smoke), weapon_smoke_puff, launcher_backblast and flame_jet, written in the weapon's own axes (x along the barrel) with the Arsenal gallery's numbers and the order it drew them in.

vfx.burst(kind, point, normal, count, speed?) and vfx.spawn(kind, point, velocity) are the sprite kinds that destruction, fluids and weapons share (debris, sparks, ash, steam, drips, leaks...: data/sprite-kinds.json, patchSpriteKinds), with onLand(owner, ...) to hear where a drop touched the ground.

effectForEvent(event) maps the destruction events to effects (data/event-effects.json): spall plays impact_<material>, the stages stage_crack, breach_dust and collapse_dust, expose rebar_expose, a fracture of glass glass_burst; muzzle_* and casing_* are the weapon slices. GAME_EFFECTS is the recipes plus these, what a world plays by default. Texts of every recipe (name, cost, when, look, budget, network) are in locales (en, pt-BR).

Vehicle effects

VEHICLE_EFFECTS write the Garagem's recipes around a car: vehicle_fire and vehicle_exhaust (engine_smoke), vehicle_leak and vehicle_leak_ignited (fuel_leak). A normal bullet through a tank only leaks, so vehicle_leak is the page's pool and drips without the fire and the explosion (FUEL_LEAK_ONLY: the same decal and emit steps); vehicle_leak_ignited is the whole sequence (the spark, the fire running along the pool to the tank and the blast at 7.2 s), played when the leak catches fire.

Closed spaces (a shut car keeps the smoke out)

world.enclose(id, { center, halfSize, orientation, openings, leak }) declares a closed space: a turned box the smoke, dust, steam and gas do not cross. It works on all three particle systems: the puffs (kinds smoke, dust and gas; not blood or bile), the sprite kinds marked carried (smoke, dust, steam, ash) and the Workshop's light normal-blend sprites (g under 3). A step that would cross a wall is put back on the wall and slides along it; it passes only inside an opening (an opening with a rate passes that share per second of what presses on it, like a bullet hole), or through a closed wall at leak per second (the gaps of a shut door). What starts inside stays inside. A puff stops about a third of its size off a wall it is outside of, so its edge does not show through the glass. Call enclose again each time the space moves or opens; enclose(id, null) ends it, and clear() keeps the spaces (they are the scene, not effects). The same shape is Enclosure of the contracts, and game-vfx-three's renderer forwards EffectSink.enclose here.

Install

pnpm add @adatechnology/game-vfx

Browser without a bundler (the global is GameVfx):

<script src="https://unpkg.com/@adatechnology/game-vfx/dist/index.global.js"></script>
<script>
  const vfx = GameVfx.createVfx({ seed: 1 })
</script>

Port invariants

Each one has a test: pools iterate in insertion order and compact in place; every puff draws its seed even when the caller gives one; chunks draw rotation and spin first and a hot one adds a halo drawn after; due timers run from the last added to the first; colours are converted once, at spawn; no pool grows past its profile. The goldens (tests/team-golden.test.ts) replay the Lab's 16 scenes against a recording of the page.