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

@zakkster/lite-hud

v2.0.0

Published

SPP-native zero-GC canvas overlay for the @zakkster profiler suite.

Readme

@zakkster/lite-hud

npm version Zero-GC sponsor npm bundle size npm downloads npm total downloads TypeScript Dependencies License: MIT

SPP-native zero-GC canvas overlay for the @zakkster profiler suite.

Single-file ESM, no runtime dependencies, phosphor-green oscilloscope aesthetic. Designed as the read-side consumer of @zakkster/lite-scope's mux registry. Also works as a drop-in stats.js replacement via hud.channel().

v2.0.0 is a breaking rewrite. See the migration guide below.


Install

npm install @zakkster/lite-hud

Scope-driven mode (primary)

import { createHud } from '@zakkster/lite-hud';
import { createScope } from '@zakkster/lite-scope';

const scope = createScope();
const hud   = createHud(document.body, { windowSec: 5, position: 'top-right' });

hud.attach(scope, {
  budgets: [
    { channel: 'frame', threshold: 16.67, label: '60fps budget' },
  ],
});

// Rendering loop -- call at ~10-15 Hz
function loop() {
  requestAnimationFrame(loop);
  if (frameCount++ % 4 === 0) hud.render();
}
loop();

attach() reads scope.streams(), builds an O(1) LUT per stream, and registers the HUD as a live mux sink via scope.addSink(). From that point every SPP record emitted by any probe flows directly into the HUD with no copy.


Manual channel mode (drop-in stats.js replacement)

No scope setup required.

const hud   = createHud(document.body);
const fps   = hud.channel({ name: 'fps',       kind: 0 }); // LEVEL
const gc    = hud.channel({ name: 'gc',        kind: 1 }); // INSTANT
const frame = hud.channel({ name: 'frame',     kind: 2 }); // SPAN (complete)
const draws = hud.channel({ name: 'draw calls', kind: 3 }); // COUNTER

// Anywhere in your code:
fps.push(59.8);
gc.push();
frame.push(12.4);   // duration in ms; t_start = now - 12.4  (D5 layout)
draws.push(312);

push() synthesizes a valid SPP record and calls hud.write() directly -- the same demux path used by scope probes. There is no separate ring or draw path.


API reference

createHud(mountEl, opts?): Hud

| Option | Type | Default | Description | |--------|------|---------|-------------| | windowSec | number | 5 | Scrolling time window width in seconds | | position | 'top-right' \| 'top-left' \| 'bottom-right' \| 'bottom-left' | 'top-right' | Canvas corner | | hotkey | string | '`' | Key that toggles visibility. '' to disable | | zIndex | number | 9999 | CSS z-index of the canvas |

Pass null as mountEl for headless / test mode. All DOM and canvas operations are skipped; the full state layer remains active.


hud.attach(scope, opts?)

Reads scope.streams(), builds the LUT, and calls scope.addSink(hud).

opts.budgets is an array of inline threshold lines applied at attach time:

{ channel: 'fps', threshold: 60, label: 'min fps' }

Budget thresholds are also transported at runtime via the BUDGET_SET (0x0F41) meta opcode: a = interned_name_id, b = threshold_value.


hud.channel(desc): { push(v?) }

Creates a manual channel. Synthetic stream IDs are assigned from 0x8000 and never conflict with scope-registered stream IDs (dense from 1).

| Field | Type | Default | |-------|------|---------| | name | string | 'ch<n>' | | unit | string | '' | | hz | number | null (256-record ring) | | kind | 0\|1\|2\|3 | 0 (LEVEL) |

push() semantics by kind:

| Kind | Call | What is stored | |------|------|----------------| | LEVEL (0) | push(value) | t=now, a=value | | INSTANT (1) | push() | t=now, a=0 | | SPAN (2) | push(durationMs) | t=now-dur, a=dur (D5) | | COUNTER (3) | push(value) | t=now, a=value |


hud.write(packed, t, a, b)

Duck-typed SPP sink entry point. Called automatically by the scope mux after attach(); also used directly by push() for manual channels.

Packed field decoding uses arithmetic (not bitwise) to handle the full u16 stream ID range:

const sid = (packed / 65536) | 0;
const op  = (packed - sid * 65536) | 0;

hud.stats()

{
  drops:        number;    // unrouted records (LUT miss or pool eviction)
  channels:     number;
  epoch:        number | null;  // t from last EPOCH (0x0F00) meta record
  verdicts:     number;    // gate verdicts stored (capped at 64)
  budgets:      number;    // total threshold lines across all channels
  channelStats: Array<{ name: string; count: number; head: number }>;
}

hud.inspect(name): { count, last: { t, a, b } } | null

Returns the most recent ring record for a named channel. Cold path -- allocates. Do not call from a frame loop.

hud.render()

Draws all visible channels. Caller-throttled to ~10--15 Hz. No-op when mountEl is null or overlay is hidden.

hud.show() / hud.hide() / hud.destroy()

destroy() removes the canvas from the DOM, removes the keydown listener, and calls scope.removeSink() if the scope provides it.


Ring layout

| Kind | Stride | Slots | |------|--------|-------| | LEVEL / INSTANT / COUNTER (width=1) | 3 | [t, a, b] | | Complete SPAN (non-paired) | 3 | [t_start, duration_ms, 0] | | Paired SPAN (closed) | 3 | [t_open, t_close, correlId] | | width=N (CONT) | 3N | primary [t, a, b] + (N-1) CONT triplets |

Ring capacity = pow2(ceil(hz × windowSec) + 1) for LEVEL channels, 256 otherwise.


Test suite

npm test
# 59 tests, 0 failures

All tests run headless (mountEl = null). The mock scope factory in the test file is a useful reference for testing your own probes against the HUD.


Migration from v1

Channel creation

// v1
const fps = hud.track('fps', { hi: 120, lo: 0, warnBelow: 30 });
fps.push(59.8);

// v2 -- manual channel
const fps = hud.channel({ name: 'fps', kind: 0 });
fps.push(59.8);
// Budget lines via attach option:
hud.attach(scope, { budgets: [{ channel: 'fps', threshold: 30, label: 'min fps' }] });

Scope integration

// v1 -- HUD polled a memory sink separately
// v2 -- HUD is a live mux sink
hud.attach(scope);           // that's it

Rendering

// v1 and v2 -- same
hud.render();  // call at ~10-15Hz

Protocol reference (SPP v1 constants, inlined)

Meta stream (sid 0):  EPOCH 0x0F00 | CONT 0x0F01 | VERDICT 0x0F40 | BUDGET_SET 0x0F41
Kinds:                LEVEL 0 | INSTANT 1 | SPAN 2 | COUNTER 3

License

MIT -- Copyright (c) 2026 Zahary Shinikchiev