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

zissl

v0.2.0

Published

The whole video synth in one file of WGSL — a Hydra-compatible engine on WebGPU.

Readme

zissl

The whole video synthesizer is one file of WGSL. Your patch compiles to a single fragment shader in milliseconds, and it runs on WebGPU.

Hydra's synth language is one of the loveliest ideas in live coding: video as signal chains — osc().rotate().modulate() — with feedback for free. But the renderer under it is WebGL: an aging API the browsers have stopped investing in, GLSL strings stitched at runtime, implicit state, context-loss roulette, and a hard ceiling the moment you want compute, storage buffers, or float precision end to end.

zissl keeps the language and replaces the machine. Every Hydra source and transform — oscillators, noise, voronoi, the geometry warps, the color maps, blends, modulators — lives in one file of WGSL, ported function-for-function from hydra-synth's glsl-functions.js with the same names, defaults and math. A patch written for Hydra means the same thing here; it just renders on the API the browsers are actually building for the next twenty years.

The sweet counterpart

zissl is the sister of zaltz (zaltz un tsuker — salt and sugar). zaltz rebuilt Strudel's sound engine as one file of C on the audio thread; zissl rebuilds Hydra's picture engine as one file of WGSL on the GPU. Same shape both times: one engine file, a thin typed host, and the language stays upstream — zaltz is the sound, zissl is the picture.

The golden gate

Like zaltz, zissl is measured against the thing it replaces — and the measurement runs itself:

npm test              # headless Chrome, both engines, the whole corpus
npm run gate:watch    # the same run, in a window you can watch

harness/ renders a 79-sketch corpus on hydra-synth (WebGL) and on zissl (WebGPU) under the same pinned clock and pixel-diffs the frames — every source and transform feature by feature, plus feedback, function params, array sequencing, multi-output wiring, external sources, and the edge values where two float pipelines are most likely to part ways. 74/74 comparable sketches match pixel for pixel — worst MAE 0.00/255.

Three things keep it honest:

  • A coverage contract. The roster is parsed out of hydra-synth's own glsl-functions.js at run time, what zissl speaks is probed off the live engine, and what the corpus exercises is recorded by running every sketch past a proxy. The three sets are subtracted and the gate fails by name — an operator that exists but is untested, or is tested but missing, is a red build, not a comment someone forgot to update. Today: all 52 of hydra's operators are here and all 52 are exercised.
  • A self-test. Every run re-renders one sketch with a small deliberate nudge and requires the gate to catch it. A gate that cannot fail proves nothing.
  • Named divergences. Where we knowingly differ, the run says so out loud rather than passing quietly: prev() (hydra samples the framebuffer it is currently writing — undefined in GL, illegal in WebGPU; ours is the previous frame, identical to src(o0)), and the whole-chain-in-a-number's-slot conversion that hydra documents but never applies (its shader doesn't compile and the output goes black; ours renders). Those are asserted too: ours must paint, and must match the spelling that says the same thing out loud.

The gate has caught real bugs every time it has been widened — orientation, shift's fract semantics, modulateRepeatY's reference quirk (kept bug-for-bug), Rec.709 luminance, nearest+clamp output sampling, a quadrupled sum, external-source filtering. That is the whole point of having one.

What's in the box

  • engine/zissl.wgsl — the synthesizer: every source and transform, plus the simplex noise, HSV and luminance helpers they lean on. Coordinate space is Hydra's (origin bottom-left), and every texture read round-trips the flip so feedback loops land pixel-true.
  • index.js — the thin host: a compiler that walks a chain back-to-front into straight-line WGSL (coord warps compose screen-first, then the source samples, then color forward — Hydra's exact semantics), the o0–o3 ping-pong feedback buffers, s0–s3 external sources (camera, video, image, screen), and per-frame param evaluation into uniform slots — numbers, () => time functions and [1,2,3].fast(2) sequences all animate with zero recompiles.
  • dist/zissl.wgsl.js — the engine wrapped as an ES module. No fetch, no bundler config, no build step for consumers.
import Zissl from "zissl";

const z = await Zissl.create({ canvas, makeGlobal: true });

// a classic Hydra feedback patch, verbatim
osc(10, 0.1, 1.4)
  .kaleid(5)
  .rotate(0, 0.1)
  .modulate(noise(3), 0.05)
  .blend(src(o0), 0.4)
  .out(o0);
render(o0);

Prefer no globals? Skip makeGlobal and speak through the instance: z.osc(10).rotate(() => z.time / 4).out(z.o1); z.render(z.o1).

Speaking Hydra

Params speak Hydra's language — same names, same defaults, same trio of dynamic forms:

  • numbersosc(40)
  • functionsosc(() => 20 + mouse.x / 30), evaluated every frame
  • sequencesosc([10, 40, 80].fast(2).smooth()), stepped on the bpm clock with .fast / .slow / .smooth / .ease / .offset / .fit

…and a fourth Hydra allows and never delivers: a whole chain in a number's slotosc(9).rotate(noise(3)), the picture read as the value (its channels, added, at the same coordinate). Hydra documents that conversion and then emits a vec4 into a float parameter, so the shader never compiles and the frame goes black; here it renders. sum([1,1,1,1]) spells it out loud if you prefer.

Outputs and sources match too: o0…o3 with render() showing one output or the 2×2 grid, prev() for the output's own last frame, s0…s3 with initCam / initVideo / initImage / initScreen, hush(), speed, bpm, time, mouse, stats.fps, and the per-frame hooks update / afterUpdate (dt in milliseconds, Hydra's signature) — as page globals under makeGlobal, so update = (dt) => { … } works exactly as it reads.

External sources sample nearest, like Hydra's own textures; pass Zissl.create({ sourceFilter: "linear" }) to trade that parity for smoother video under a coordinate warp.

Custom transforms are setFunction, same shape as Hydra's — except the body is WGSL now, which is the honest price of the new machine:

z.setFunction({
  name: "vignette",
  type: "color",
  inputs: [{ name: "amount", default: 1 }],
  wgsl: `fn vignette(c0: vec4f, amount: f32) -> vec4f {
    return c0; // your math here — U.time, U.res and the z_ helpers are in scope
  }`,
});

Audio reactivity is Hydra's a, with one upgrade: a.init() listens to the microphone like Hydra, but a.init({ source }) taps any MediaStream, media element, or WebAudio node — hand it your engine's output node and the visuals react to what's actually playing, no mic loopback:

a.init({ source: zaltzEngine.node }); // zaltz's AudioWorkletNode — or any AudioNode
osc(10).brightness(() => a.fft[0]).out(o0);
a.setBins(6).setSmooth(0.7); a.show(); // the familiar knobs + bars overlay

Also aboard: fps (render-rate cap), screencap() (PNG of the current frame), readPixels() (honest ImageData readback — WebGPU canvases don't readback through 2d drawImage; this is also the future pixel-diff harness against Hydra's renders).

Not here yet, said plainly: the p5/GLSL extension ecosystem — setFunction speaks WGSL, not GLSL. The language core is complete.

Strudel — the H method

This is the pairing zissl was born for. In Klappn's live sets, all motion comes from H(pat) — a Strudel pattern sampled on the transport clock, in cycles, so the picture is locked to the music's own time, not the wall's. zissl builds that bridge in:

const z = await Zissl.create({ canvas, makeGlobal: true });

z.setTime(() => scheduler.now());   // your transport, in CYCLES —
                                    // the same clock zaltz plays from

osc(4, 0, 1)                        // Hydra's own clocks frozen (explicit 0s)…
  .rotate(H(saw.slow(8)))           // …ALL motion rides the pattern
  .scale(H("<1 1.5 2>"))            // works with anything that has queryArc
  .out(o0);

H() duck-types: a Strudel pattern (anything with queryArc), a plain function of cycle time, or a number. No @strudel/* dependency — and if you already use @strudel/hydra, its H works against zissl unchanged, because zissl params accept the same zero-arg thunks. Strudel's feedStrudel trick works too: s0.init({ src: strudelDrawCanvas }).

Mini-notationH("<0!4 1!8>"), the natural way to gate a section — needs a parser, and zissl deliberately doesn't carry one. Hand it Strudel's:

import { reify } from "@strudel/core";
z.setReify(reify);                  // now H("<6 8 12 16>") is a real pattern

Without it a string is just a string, and H reads a flat 0 — silently, which is the worst kind of quiet. One line, and the whole notation is in scope.

Try it

python3 -m http.server 8095 -d .
# → http://localhost:8095/example/

Any browser with WebGPU (Chrome/Edge 113+, Safari 26+, Firefox 141+). There is deliberately no WebGL fallback — Hydra itself is the WebGL path, and it's a good one.

License

AGPL-3.0-or-later, inherited with gratitude from hydra-synth — see NOTICE.md.