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

pretext-pixel

v0.2.1

Published

Pretext-inspired pixel animation library: compile once, paint cheap, render anywhere.

Readme

PixelFlow

Tiny pixel animations for marketing pages, loading indicators, and dashboard widgets. ESM, no GPU, themes via palette swap. ~22 kB packed, zero runtime dependencies.

v0.2.1 adds paintRaster — a pre-rasterized + drawImage paint strategy for rendering many instances of the same sprite. Pairs with the existing diff-op paintCanvas so you pick per use case. See Renderers.

Live demo → with diff visualizer, compile-vs-paint timing chart, palette swap benchmark, stress field, and PNG drop import.

When to use

Decorative web animations — mascot loops, hero-section accents, hover effects, idle indicators ✅ Dashboard / SaaS widgets — status pulses, build/deploy indicators, activity sparkline characters ✅ Component library mascots that need light/dark themingwithPalette() swaps colors in microseconds ✅ Anywhere the same animation needs Canvas and SVG and ASCII — CI logs, snapshot tests, terminal UIs

Games — use Phaser or PixiJS, they have WebGL and physics ❌ Photographic content — quantization is naive, use a real image pipeline ❌ Thousands of distinct sprites — single-instance compile cost adds up

Why

Most pixel animation code redraws the whole grid every frame. PixelFlow borrows chenglou/pretext's setup/hot-path split: the heavy work (palette extraction, frame-to-frame diffing, packing) happens once in compile(), and per-frame painting is reduced to walking a packed diff-op buffer. The same compiled state can drive Canvas, SVG, ASCII, or any custom renderer in parallel.

| pretext | PixelFlow | |---|---| | prepare() (one-time, heavy) | compile() | | layout() (cheap, per-call) | paintCanvas() / paintSVG() / paintAscii() | | LayoutCursor (segment+grapheme) | FrameCursor | | walkLineRanges() | measureFrameBounds() | | setLocale() (palette/cache reset) | withPalette() / clearCache() | | same prepared → multiple consumers | same compiled → canvas + svg + ascii |

Install

npm install pretext-pixel

Published as pretext-pixel on npm — the project name PixelFlow was too similar to an existing package, so the registry name references the inspiration (chenglou/pretext) instead.

Quick start

import {
  compile, prepareSVG,
  paintCanvas, paintSVG, paintAscii,
  createAnimator,
} from 'pretext-pixel';

// Author a sprite with a char grid + palette.
// '.' is reserved for transparent.
const sprite = compile({
  frames: [
    [
      "..RRRR..",
      ".RRRRRR.",
      ".R.RR.R.",
      "..RRRR..",
    ],
    [
      "..RRRR..",
      ".RRRRRR.",
      ".RRRRRR.",
      "..R..R..",
    ],
  ],
  palette: { R: '#dc2626' },
  speed: 120,  // ms per frame
});

// Hook up any number of paint targets — all read the same compiled state.
const ctx = canvas.getContext('2d');
const svgState = prepareSVG(svgEl, sprite);

const anim = createAnimator(sprite, [
  c => paintCanvas(ctx, sprite, c),
  c => paintSVG(svgState, sprite, c),
  c => paintAscii(preEl, sprite, c),
]);

anim.start();

Core concepts

compile(source)CompiledSprite

Validates input, builds a palette (index 0 is always transparent), converts each frame to a packed Int8Array of palette indices, and computes inter-frame diff ops as Int16Arrays of [x, y, colorIndex, ...]. Throws on uneven frame dims, unknown palette chars, or '.' in the palette.

Includes stats:

  • frameCount, totalCells, totalDiffOps
  • compressionRatio — fraction of cells skipped vs full redraw (typically 0.7+)
  • compileMs

Cursors

A FrameCursor is just { frameIndex }. Use cursorStart, nextCursor(), stepCursor() to navigate. Mirrors pretext's LayoutCursor.

Renderers

All renderers read sprite.diffs[cursor.frameIndex] and update only changed cells. Add your own (WebGL, terminal, server-side PNG) by following the same pattern: walk the flat [x, y, colorIndex, ...] buffer.

  • paintCanvas(ctx, sprite, cursor) — diff-op based; walks the packed [x, y, color] buffer for the current frame and fillRects only changed cells. Cheapest per-frame cost when most cells stay constant. Scale via CSS with image-rendering: pixelated.
  • paintRaster(ctx, sprite, cursor, { dx, dy }) — pre-rasterizes each frame to an offscreen canvas (lazy, cached per sprite) and paints with one drawImage call. Trades per-cell granularity for batched GPU-accelerated blits — use this when rendering many instances of the same sprite.
  • prepareSVG(host, sprite) + paintSVG(state, sprite, cursor) — one <rect> per pixel created upfront, only fill attribute updated per frame.
  • renderAscii(sprite, cursor) / paintAscii(pre, sprite, cursor) — luminance-mapped block characters ( ░▒▓█).

Picking between paintCanvas and paintRaster: start with paintCanvas for single sprites with mostly-static content (the diff visualizer in the demo shows the savings live). Switch to paintRaster when you need many instances — at 5000 sprites of 48×48, the diff path drops below 30 fps while raster sustains 60.

Inspection

  • measureFrameBounds(sprite, cursor) — bbox of non-transparent pixels for one frame (think pretext's measureNaturalWidth).
  • measureUnionBounds(sprite) — bbox covering every frame; useful for collision shapes or tight cropping.

Theme swap

const damaged = withPalette(sprite, originalSource, {
  B: '#7f1d1d',  // remap blue to dark red
  L: '#dc2626',
});

Returns a new compiled sprite that reuses diffs and grids by reference — the operation is O(palette size), not O(pixels). Run damaged/night/inverted variants without recompiling.

Memoization

import { compileMemo, clearCache } from 'pretext-pixel';
const sprite = compileMemo('hero-walk', source);  // cached by key
clearCache();

Animator

Drives playback. Bind any number of paint callbacks; all run on each tick with the same cursor. Supports start, stop, seek, step, redraw for scrub controls and timelines.

Image import

Convert a PNG/sprite-sheet into a SpriteSource:

import { importImage, compile } from 'pretext-pixel';

const img = new Image();
img.src = 'hero-walk.png';
img.onload = () => {
  const { source } = importImage(img, { rows: 1, cols: 4, maxColors: 12 });
  const sprite = compile(source);
  // ...
};

Frequency-based color quantization to single-char palette keys. Alpha below the threshold becomes '.'.

API summary

| function | purpose | |---|---| | compile(source) | one-time analysis + diff compression | | compileMemo(key, source) / clearCache() | cached variant | | cursorStart, nextCursor, stepCursor | position arithmetic | | paintCanvas(ctx, sprite, cursor, opts?) | Canvas2D diff-op paint | | paintRaster(ctx, sprite, cursor, opts?) | Canvas2D pre-raster + drawImage (many instances) | | prerasterize(sprite) / clearRasterCache(sprite?) | manage the raster cache | | prepareSVG(host, sprite) / paintSVG(state, sprite, cursor) | SVG paint | | renderAscii(sprite, cursor) / paintAscii(el, sprite, cursor) | ASCII paint | | measureFrameBounds, measureUnionBounds | non-transparent bbox | | withPalette(sprite, source, remap) | palette swap, structure-shared | | createAnimator(sprite, paintFns, opts?) | playback driver | | importImage(img, opts?) | PNG → SpriteSource with quantize |

Running the demo locally

git clone https://github.com/Daniel-Nexus/pixelflow.git
cd pixelflow
npm install
npm run build
python3 -m http.server 8765   # or any static server
# open http://localhost:8765/demos/index.html

Or visit the hosted version: https://daniel-nexus.github.io/pixelflow/demos/

License

MIT


Made by NEXUS AI Labs.