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

magicsmoke

v0.5.0

Published

Sparks, arcs, pops and hum: hardware failing on a web page, in three.js

Readme

magicsmoke

Hardware failing on a web page: sparks, electrical arcs, pops and crackle, a mains hum, flashes of light, the element jerking, the phone buzzing. Built on three.js and three.quarks, with every sound synthesized live and no image or audio assets.

Install

npm i magicsmoke three

A page with no three.js scene

createOverlay puts a transparent canvas over the viewport and takes client coordinates.

import { createOverlay, dwell } from 'magicsmoke';

const fx = createOverlay({ sound: true, jolt: sign });
const fault = fx.fault({ at: { x: 0, y: 0 } });

// The longer the pointer rests in one spot on the sign, the harder it fails; moving drains it.
dwell(sign, { rise: 1500, touchRise: 600, fall: 600 }).onChange(({ value, x, y }) => {
  fault.intensity = value;
  fault.at = { x, y };
});

One-shots fire a single discharge: fx.sputter(at), fx.burst(at), fx.shower(at) and fx.arc(from, to), each with an optional energy from 0 to 1.

Inside a three.js scene

createLayer returns an object to add to your scene and an update to call each frame. It takes world coordinates, y up.

import { createLayer } from 'magicsmoke';

const layer = createLayer({ sound: true, scale: 0.01 }); // world units per CSS pixel
scene.add(layer.object);

const fault = layer.fault({ at: new Vector3(0, 1, 0), to: new Vector3(0.5, 1, 0) });
fault.intensity = 0.7;

renderer.setAnimationLoop((time) => {
  layer.update(clock.getDelta());
  renderer.render(scene, camera);
});

A fault given a to point arcs to it. fault.blow({ peak, after }) overloads a fault: it discharges ever faster and harder while the element shakes, throws a volley of showers at the climax peak milliseconds later, and goes dead on that frame, taking no intensity for after. Time peak to a sign's own flare. layer.whine, from 0 to 1, is a high ballast whine for a tube striking back on: it comes in at once and fades out when set back to 0. layer.live is false once nothing is moving, lit or sounding, so a host can stop rendering.

On blits

Every fault runs as a voice on blits, which mixes concurrent effects. createSmoke and createSmokeOverlay are that engine's own interface: a fault's weight is its intensity, as a number or any blits signal, and the host reports the clock once a frame. createLayer and createOverlay above are the same engine behind the interface it had before.

import { createSmoke, dwell, fault } from 'magicsmoke';

const smoke = createSmoke({ sound: true, scale: 0.01 });
scene.add(smoke.object);

const f = smoke.cue(fault({ at, to }), { weight: dwell(sign, { rise: 1500, fall: 600 }) });
renderer.setAnimationLoop((time) => {
  smoke.sync(time);
  renderer.render(scene, camera);
});
document.addEventListener('visibilitychange', () => document.hidden || smoke.rebase());

f.fade({ over: 300 }); // winds down to silence, then leaves

A fault cued with no weight plays at full. While it blows it ignores its weight, and follows it again once the blow is over; through createLayer it stays dead until intensity is written, as it always has. dwell is callable, so it is a signal as it stands.

Options

| Option | Default | Effect | | --- | --- | --- | | sound | false | true, or { volume, muted, mains: 50 \| 60 } | | jolt | none | An element that jerks with each discharge | | haptics | false | Vibrates with each discharge where the browser supports it | | pageFlash | false | A white full-page pulse on the largest discharges | | floor | viewport bottom (overlay), none (layer) | Where showers and sputter bounce | | seed | random | Makes the sequence of discharges repeatable | | reducedMotion | 'respect' | 'ignore' keeps jolt, lights and page flash under reduced motion | | tuning | DEFAULT_TUNING | Every effect's settings and its from threshold, editable live; TUNING_SCHEMA describes each one for a lab | | onDischarge | none | Called for every discharge, to drive an effect of your own |

What browsers allow

  • Sound waits for a click, tap or keypress. Hovering does not unlock audio, so a visitor who goes straight to a hover effect sees sparks in silence until they first click somewhere. Discharges before then make no sound; they are not saved up.
  • Vibration works only in browsers that implement navigator.vibrate, which excludes iOS Safari, and only after the page has had a user gesture.
  • Reduced motion turns off jolt and a blow's shudder, point lights and page flash, and halves the sparks. Sound stays.
  • Where WebGL is unavailable the overlay reports supported: false and every call does nothing.

Versions

Below 1.0.0 a release may change the API in a minor version. From 1.0.0 on, magicsmoke follows semver: a breaking change takes a major version.

Development

npm run dev           # the lab: one-shots, a standing fault, a hover target, live tuning
npm run bench         # per-frame CPU cost at 1 to 1000 faults
npm test              # Node tests
npm run test:browser  # Playwright: rendered pixels and offline-rendered voices
npm run check         # lint, typecheck, Node tests

The design is in docs/superpowers/specs/2026-09-14-magicsmoke-design.md.