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

h5dmd

v3.0.2

Published

WebGPU-based virtual DMD (dot-matrix display) rendering library for the browser.

Readme

H5DMD

CI

A Virtual Dmd (Dot Matrix Display) powered by HTML5 Canvas and WebGPU(optional)

256x78 Dmd on a 1280x390 display

Quick demonstration videos (tests before using mpf):

https://youtu.be/ItiX97USKyU

https://www.youtube.com/watch?v=MtAN4vKRLQ0

https://www.youtube.com/watch?v=MaJZQCTSiOg

https://www.youtube.com/watch?v=q58dZAbNXe8

Requirements

https://caniuse.com/webgpu

WebGPU now ships by default in modern browsers (Chrome/Edge 113+), so the GPU-enabling flags that were once required (such as --enable-unsafe-webgpu, --enable-features=Vulkan,UseSkiaRenderer, --enable-gpu-rasterization) are no longer needed.

The demo plays a video without a user gesture, so depending on your platform you may still want to launch the browser with autoplay enabled:

--autoplay-policy=no-user-gesture-required
--ignore-autoplay-restrictions

Live demo

https://bzhmaddog.github.io/H5DMD/index.html

If it doesn't load check the developer console for errors

Examples

import {
    CanvasLayer,
    ChromaKeyRenderer,
    Dmd,
    DmdOptions,
    DotShape,
    Easing,
    LayerGroup,
    rendererEntry,
    ShakyRenderer,
    TextLayer,
} from 'h5dmd'

const canvas = document.getElementById('output') as HTMLCanvasElement

const dmd = new Dmd(canvas, {
    dotSize: 4,
    dotSpace: 1,
    dotShape: DotShape.Square,
    backgroundBrightness: 14,
    brightness: 1,
    showFPS: true,
})

await dmd.init() // set up the renderers (WebGPU when available)
dmd.run() // start the render loop

// Add a canvas layer and draw an image into it
dmd.addLayer(CanvasLayer, 'bg', {}, layer => {
    fetch('background.png')
        .then(response => response.blob())
        .then(createImageBitmap)
        .then(bitmap => layer.setDrawFunction(({ drawBitmap }) => drawBitmap(bitmap)))
})

// Add a layer with a renderer declared up-front in the options.
// rendererEntry() infers the params type from the class, so excess/wrong-typed
// properties are caught at compile time.
dmd.addLayer(
    VideoLayer,
    'video-chroma',
    {
        autoplay: true,
        renderers: [rendererEntry('chroma', ChromaKeyRenderer, { color: [0, 255, 0], threshold: 20 })],
    },
    layer => {
        const video = document.createElement('video')
        video.addEventListener('loadeddata', () => layer.setVideo(video))
        video.src = 'green-screen.webm'
    },
)

// Or add a renderer after construction using the async addRenderer() method.
// The loaded callback accepts async/await so you can await initialisation.
dmd.addLayer(CanvasLayer, 'shaky', {}, async layer => {
    // await ensures the renderer is fully initialised before drawing starts
    await layer.addRenderer('shake', ShakyRenderer, { intensity: 2, speed: 8, mode: 'sine' })
    layer.setDrawFunction(({ fillColor }) => fillColor('#FF0000'))
    layer.draw()
})

// LayerGroup is a "virtual" layer: it can be dimensioned, positioned, given renderers,
// a background color/opacity and shown/hidden like any other layer, but it cannot draw
// content itself - instead it composites a set of child layers (which may themselves be
// LayerGroups, nested arbitrarily deep).
const hud = dmd.addLayer(LayerGroup, 'hud', {
    width: 80,
    height: 20,
    position: { hAlign: 'right', vAlign: 'top' },
    backgroundColor: '#000000',
    backgroundOpacity: 0.5,
})
hud.addLayer(TextLayer, 'score', { text: '0', position: { left: 2, top: 2 } })
hud.addLayer(CanvasLayer, 'icon', { position: { left: 60, top: 2 } }, layer => {
    layer.fillColor('#FFFFFF')
})

// Hiding a group cascades to its children (pausing e.g. a child VideoLayer's playback) and
// restores each child's own prior visibility when the group is shown again.
hud.setVisibility(false)
hud.setVisibility(true)

// Position a layer relative to a sibling instead of the container, via hAlign/vAlign:
// 'constraint' and a *To*Of field naming both the relationship and the target (a sibling's
// id, already added to the same container, or 'parent' - the container itself, the default).
hud.addLayer(TextLayer, 'label', {
    text: 'SCORE',
    position: {
        hAlign: 'constraint',
        leftToRightOf: 'icon', // my left edge, against icon's right edge
        vAlign: 'constraint',
        topToTopOf: 'icon', // my top edge, against icon's top edge
    },
})

// Fade a layer in/out (operates on layer opacity)
const layer = dmd.getLayer('bg')
await layer.fadeOut(1000) // fade out over 1s (default easing: easeOutSine)
await layer.fadeIn(500, Easing.easeInSine) // fade in with custom easing

// Or use the Dmd convenience methods (handles visibility automatically)
await dmd.fadeLayerOut('bg', 1000)
await dmd.fadeLayerIn('bg', 500)

// Fade the entire DMD brightness
await dmd.fadeOut(1000)
await dmd.fadeIn(1000)

For a complete example see Demo/advanced/main.ts.

Install dependencies

npm install

Build and watch

#Transpile all files
npm run build

#Watch file changes
npm run watch

#Run eslinter
npm run lint

#Run tests
npm run test

#Build documentation
npm run build:docs

Running the demo locally

The Demo app imports this library as the h5dmd package. To run it against your local build, expose the library through a global link:

# From the repository root: build the library so dist/ exists, then link it
npm install
npm run build
npm link

Then link it inside the demo and start the dev server:

cd Demo
npm install
npm link h5dmd
npm run dev

See Demo/README.md for more details.

Documentation

Follow this link

Releasing

The version number lives in package.json, in src/dmd.ts (Dmd.version) and in the git release tag. Use npm version to bump all of them in one step — a version lifecycle script (scripts/sync-version.mjs) rewrites Dmd.version and stages it so it lands in the same commit as package.json:

# bump patch / minor / major (creates the commit and the vX.Y.Z tag)
npm version patch

# push the commit and the tag
git push --follow-tags

Then publish a GitHub Release pointing at the new tag. That triggers the Release workflow, which verifies that the tag, package.json and Dmd.version all match before publishing to npm.