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

@tensordoc/prism

v0.1.4

Published

Two-line embed for audio-reactive visualizations. Milkdrop + Shadertoy in a single PrismPlayer class.

Readme

@tensordoc/prism

Two-line embed for audio-reactive visualizations. Drop a <div>, call new PrismPlayer({ container }), get a Milkdrop preset or Shadertoy fragment shader playing in the browser — reacting to whatever audio you connect.

▶ Live demo at prism.scott.ai/examples/embed.html. Fork in CodeSandbox or StackBlitz.

npm install @tensordoc/prism
<div id="viz" style="width:100vw;height:100vh"></div>
<script type="module">
  import { PrismPlayer } from "@tensordoc/prism";
  new PrismPlayer({ container: "viz" });
</script>

That's it. The visualization runs against a built-in synthetic signal until you connect real audio.

Live demo

prism.scott.ai — the deployed site uses this exact package. The "Two-line embed" preview at prism.scott.ai/examples/embed.html is the smallest possible HTML page using it.

What you get

Two rendering backends that swap automatically based on the graph you load:

  • Milkdrop via butterchurn — ~80 named presets ship in the bundle
  • Shadertoy — any GLSL fragment shader following Shadertoy's mainImage() convention; loaded from a URL

Six audio sources the player accepts:

new PrismPlayer({
  container: "viz",
  audio: "mic",                       // getUserMedia
  audio: "tab",                       // getDisplayMedia
  audio: someAudioNode,               // your Web Audio graph
  audio: someMediaStream,             // anything with audio tracks
  // audio: undefined (default)       → built-in synthetic signal
});

Six image sources for shader inputs (iChannel1):

new PrismPlayer({
  container: "viz",
  image: "https://example.com/a.jpg",        // single URL
  image: ["a.jpg", "b.jpg", "c.jpg"],        // built-in crossfading slideshow
  image: "webcam",                            // getUserMedia({video: true})
  image: "tab",                               // getDisplayMedia({video: true})
  image: someVideoElement,                    // <video>
  image: someCanvas,                          // your own renderer
});

// Tune slideshow timing
new PrismPlayer({ container, image: ["a.jpg","b.jpg"], holdSeconds: 6 });

Methods for runtime control:

player.load(graphOrShortId);     // swap visualization
player.connectAudio("mic");      // change audio source
player.disconnectAudio();        // revert to synthetic signal
player.connectImage("webcam");   // change image feed
player.disconnectImage();        // release webcam track, etc.
player.destroy();                // clean up everything

Readonly handles for power users (the underlying primitives are exposed; you can call them directly):

player.audioCtx        // AudioContext
player.activeBackend   // "milkdrop" | "shadertoy"
player.milkdrop        // butterchurn handle
player.shadertoy       // shader runtime
player.synth           // synthetic signal driver
player.runtime         // graph executor

Share-by-URL with short IDs

Every curated catalog entry has a permanent 6-character base62 share token. Same idea as YouTube video IDs:

new PrismPlayer({ container: "viz", graph: "7Hq3pK" });

The lookup happens against the bundled registry — no network call. The companion site exposes prism.scott.ai/?g=<id> as a one-click shareable URL.

import { lookup, shortIdToGraph } from "@tensordoc/prism";
lookup("7Hq3pK");          // { name, source_type, source_url, ... }
shortIdToGraph("7Hq3pK");  // ready-to-use PrismGraph

Constructor options (full)

new PrismPlayer({
  container: HTMLElement | string,          // required — DOM id or element
  graph?: PrismGraph | string,              // short_id or full graph
  audio?: "mic" | "tab" | MediaStream | AudioNode,
  audioCtx?: AudioContext,                  // bring your own
  image?: ImageSource,                      // see "image sources" above
  holdSeconds?: number,                     // slideshow timing, default 6
  defaultImage?: string,                    // static fallback URL
  initialPresetName?: string,               // cold-open Milkdrop preset
  onReady?: () => void,                     // first-frame callback
  onError?: (err: Error) => void,
});

Optional: draggable picture-in-picture overlay

If you want a Picture-in-Picture viewer for the slideshow, ship with ImageOverlay — a separate export that gives you a draggable, resizable, collapsible card. The player stays headless; you compose:

import { ImageOverlay } from "@tensordoc/prism";
const overlay = new ImageOverlay({ mount: document.body });
// overlay.element is a DOM div you can position or style;
// overlay.rect is the current rectangle, updated on drag/resize

Use from a CDN (no install)

<script type="module">
  import { PrismPlayer } from "https://esm.sh/@tensordoc/prism";
  new PrismPlayer({ container: "viz" });
</script>

Audio context note

Browsers require a user gesture before audio starts. The player creates an AudioContext but leaves it suspended; resume it on the first interaction:

const resume = () => player.audioCtx.resume();
window.addEventListener("pointerdown", resume, { once: true });
window.addEventListener("keydown", resume, { once: true });

Bundle size

ESM ~25 KB gzipped (excluding butterchurn). Butterchurn itself ships in the bundle as a regular dependency (~500 KB). If you don't need Milkdrop and only want shader visualizations, you can tree-shake butterchurn out by importing only what you use:

import { createShadertoyBackground } from "@tensordoc/prism";

License

MIT