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

@phyrex/ascii-canvas

v1.0.1

Published

Framework-agnostic Web Component that converts any source canvas/image/video into ASCII art.

Readme

@phyrex/ascii-canvas

banner

Drop-in Web Component that turns any image, video, or canvas into live ASCII art.

   +------------------+        +-----------------------+
   |   <img>          |        |                       |
   |   <video>        |  --->  |   <ascii-canvas>      |
   |   <canvas>       |        |                       |
   +------------------+        +-----------------------+
       any source                   ASCII output

Live demo → · Live2D variant →

showcase

  • ~2.3 KB gzipped, ESM only
  • Zero deps — no React, Vue, three.js, or pixi
  • Works in any framework via Custom Elements
  • Auto-pauses when scrolled offscreen, respects prefers-reduced-motion

Quick start

CDN

<ascii-canvas id="ac" style="width: 320px; aspect-ratio: 1/1;"></ascii-canvas>
<script type="module">
  import "https://esm.sh/@phyrex/ascii-canvas";

  const img = new Image();
  img.crossOrigin = "anonymous";
  img.src = "https://example.com/cat.jpg";
  img.onload = () => { document.getElementById("ac").source = img; };
</script>

npm

npm install @phyrex/ascii-canvas
import "@phyrex/ascii-canvas";

const ac = document.querySelector("ascii-canvas");
ac.source = someCanvasOrVideoOrImage;

The element registers itself as <ascii-canvas> on import. Set its CSS size; the grid is computed from width / cell-w × height / cell-h.


API

Attributes

| Attribute | Default | Description | |---|---|---| | ramp | " .:-=+*#%@" | Character set ordered dark→bright | | cell-w | 8 | Grid cell width in CSS pixels | | cell-h | 14 | Grid cell height in CSS pixels | | font-px | 12 | Output font size | | alpha-threshold | 16 | Skip cells whose source alpha is below this (0–255) | | paused | (absent) | When present, freezes rendering |

All attributes reflect to #opts via attributeChangedCallback. Changing cell-w / cell-h recomputes the grid.

Properties

| Property | Type | Description | |---|---|---| | source | HTMLImageElement \| HTMLVideoElement \| HTMLCanvasElement \| null | The drawable source. Setting null clears the output and stops the rAF loop. |

Methods

| Method | Description | |---|---| | pause() | Equivalent to setAttribute("paused", "") | | resume() | Equivalent to removeAttribute("paused") |

There is no getText(), no theming API, and no per-frame events. By design — see tasks.md D8/D9.

Lifecycle

The element automatically:

  • Recomputes the grid via ResizeObserver when its container changes size
  • Pauses the rAF loop when scrolled out of view (IntersectionObserver)
  • Renders only one frame when prefers-reduced-motion: reduce
  • Handles DPI: output canvas pixel dimensions are scaled by devicePixelRatio so text stays sharp on retina displays

How it works

  source                sample canvas              ascii ctx
  +------+   drawImage  +------+    getImageData   +-----+
  | img  |  ---------> | gridW |  ----------->    | drawAscii |
  | vid  |  (downscale) | gridH |   (RGBA bytes)   | (47 LOC)  |
  | cnv  |              +------+                   +-----+
  +------+                                              |
                                                        v
                                                output <ascii-canvas>

The 47-line core is a pure function: takes an ImageData already at grid resolution, picks a ramp character per cell by luminance, and fillTexts it onto the output canvas. Everything else is the wrapper — ResizeObserver, IntersectionObserver, prefers-reduced-motion, attribute reflection, DPR, source race handling.


Caveats

CORS

<img> from a different origin will not throw on drawImage, but getImageData (which the lib uses internally) will throw SecurityError: Tainted canvases may not be exported.

Fix: add crossorigin="anonymous" to the <img> and ensure the server returns proper CORS headers.

<img src="https://example.com/cat.jpg" crossorigin="anonymous">

SSR

Custom Elements need a DOM. In server-side rendering frameworks:

  • Astro: import in a <script> tag or use client:load directive
  • Next.js: import inside a useEffect or use next/dynamic with { ssr: false }
  • Nuxt / SvelteKit: defer to client-only context

The element does nothing until connectedCallback fires in a browser.


Comparison

| Project | Status (May 2026) | Framework lock-in | Live source | |---|---|---|---| | drei <AsciiRenderer> | Active | three.js + react-three-fiber | Three.js scene only | | aalib.js | Last commit 2018 | None | Image / video | | textmode.js | Active | None | Image only, no Custom Element | | ts-ascii-engine | Young (<1yr) | None | Canvas | | ascii-image | Dead | None | Image only | | @phyrex/ascii-canvas | This | None | Image / video / canvas, Web Component |


Development

git clone https://github.com/phyrextsai/ascii-canvas
cd ascii-canvas
npm install
npm test                          # build + types + size + visual
npm run update-baseline           # regenerate Playwright snapshot locally
node assets/gen-banner.mjs > assets/banner.svg   # regenerate the README banner

CI runs all tests on Linux. The Playwright baseline is therefore Linux-only; macOS / Windows baselines are gitignored. To regenerate the Linux baseline without a Linux machine, trigger the update-baseline workflow in GitHub Actions.


License

MIT © 2026 Phyrex Tsai