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

pngcraft

v1.0.1

Published

High-performance indexed PNG encoder & decoder with custom palettes, adaptive filters, and configurable bit depths. Zero dependencies in browsers.

Downloads

276

Readme

pngcraft

Modern, high-performance PNG encoder, decoder, & APNG toolkit for browsers and Node.js.

  • Multi-Format Encoder: 1/2/4/8-bit indexed, 24/48-bit RGB, 32/64-bit RGBA, 1–16 bit Grayscale.
  • Universal Decoder: Decodes all standard PNG color types (Indexed, Truecolor, Grayscale, RGBA) + Adam7 interlacing.
  • APNG (Animated PNG): Full animated PNG encoding & decoding (acTL, fcTL, fdAT).
  • Auto-Quantization & Dithering: Median Cut palette generator + Floyd-Steinberg error-diffusion dithering.
  • Metadata Toolkit: Embed, fast-read, and non-destructively inject tEXt, pHYs (DPI), gAMA, and sRGB metadata.
  • Adam7 Interlacing: Progressive 7-pass interlacing support for encoders and decoders.
  • Fault-Tolerant & Secure: ignoreCrc mode for corrupt PNG recovery + maxDimensions decompression bomb protection.
  • Zero Browser Dependencies: Uses native CompressionStream / DecompressionStream.
  • Node.js 18+ Support: Native streams with optional pako peer dependency fallback.

Installation

npm install pngcraft

For legacy Node.js environments (<18) or older browsers without Compression Streams API:

npm install pngcraft pako

Quick Start

1. Basic Encoding & Decoding

import { encode, decode, PALETTES } from 'pngcraft';

// Encode RGBA pixel data to 1-bit mask PNG
const pngBytes = await encode(rgbaData, width, height, {
    palette: PALETTES.MASK,
    filterStrategy: 'adaptive',
});

// Decode PNG bytes back to RGBA
const { width, height, data, palette } = await decode(pngBytes);

2. High-Res / High-Bit-Depth Formats (24, 32, 48, 64-bit)

import { encode } from 'pngcraft';

// 24-bit Truecolor RGB (8 bits/channel)
const rgb24 = await encode(rgbaData, width, height, { colorFormat: '24' });

// 32-bit Truecolor RGBA (8 bits/channel)
const rgba32 = await encode(rgbaData, width, height, { colorFormat: '32' });

// 48-bit High-Res RGB (16 bits/channel, Uint16Array input)
const rgb48 = await encode(uint16Data, width, height, { colorFormat: '48' });

// 64-bit High-Res RGBA (16 bits/channel, Uint16Array input)
const rgba64 = await encode(uint16Data, width, height, { colorFormat: '64' });

// 16-bit Grayscale
const gray16 = await encode(uint16Data, width, height, { colorType: 'grayscale', bitDepth: 16 });

3. Auto Palette Quantization (Median Cut) & Dithering

Automatically generate an optimal 256-color palette from any 32-bit RGBA image and apply Floyd-Steinberg error-diffusion dithering to eliminate gradient banding:

import { encode } from 'pngcraft';

const indexedPng = await encode(rgbaData, width, height, {
    colorType: 'indexed',
    bitDepth: 8,
    autoQuantize: true, // Median Cut optimal palette generation
    dither: true,       // Floyd-Steinberg error diffusion dithering
});

4. APNG (Animated PNG)

Encode multiple frame buffers into an Animated PNG with custom frame delays:

import { encodeAPNG, decodeAPNG } from 'pngcraft';

const frames = [
    { data: frame1Rgba, width: 200, height: 200, delay: 200 }, // 200ms
    { data: frame2Rgba, width: 200, height: 200, delay: 500 }, // 500ms
];

// Encode APNG (0 = loop infinitely)
const apngBytes = await encodeAPNG(frames, { numPlays: 0 });

// Decode APNG back to individual frames
const { numPlays, frames: decodedFrames } = await decodeAPNG(apngBytes);

5. Metadata & DPI (Physical Pixel Density)

Embed metadata or non-destructively inject/read metadata in <1ms without re-encoding pixel payloads:

import { encode, readMetadata, injectMetadata } from 'pngcraft';

// 1. Embed metadata during encode
const png = await encode(rgbaData, width, height, {
    text: { Title: "My Artwork", Author: "Artist" },
    dpi: 300, // 300 DPI physical resolution
    gamma: 0.45455,
});

// 2. Fast-read metadata without IDAT decompression (<1ms)
const meta = readMetadata(png);
console.log(meta.text.Title); // 'My Artwork'
console.log(meta.dpi);        // 300

// 3. Non-destructively inject/update metadata in existing PNG buffer
const updatedPng = injectMetadata(png, {
    text: { Copyright: "2026 pngcraft" },
    dpi: 600,
});

6. Adam7 Progressive Interlacing

import { encode, decode } from 'pngcraft';

// Encode 7-pass Adam7 interlaced PNG
const interlacedPng = await encode(rgbaData, width, height, { interlace: true });

// Decode interlaced PNG
const decoded = await decode(interlacedPng);
console.log(decoded.interlace); // 1 (Adam7)

API Reference

encode(pixelData, width, height, options?)

| Option | Type | Default | Description | |--------|------|---------|-------------| | colorType | number \| string | 'indexed' | 'indexed' (3), 'rgb' (2), 'rgba' (6), 'grayscale' (0), 'grayscale-alpha' (4) | | colorFormat | string | — | Short alias: '24', '32', '48', '64', 'rgb24', 'rgba32', 'rgb48', 'rgba64' | | palette | PaletteEntry[] | PALETTES.MASK | Custom palette entries for indexed color | | autoQuantize | boolean | false | Auto-generate optimal palette using Median Cut algorithm | | dither | boolean | false | Apply Floyd-Steinberg error diffusion dithering | | interlace | boolean | false | Encode using 7-pass Adam7 progressive interlacing | | bitDepth | number | Auto-detected | Bits per channel (1, 2, 4, 8, 16) | | filterStrategy | string \| number | 'adaptive' | Filter strategy ('adaptive', 'none', 'sub', 'up', 'average', 'paeth') | | text | Record<string, string> | — | Key-value text metadata (tEXt) | | dpi | number \| {x, y} | — | Physical pixel density in DPI (pHYs) |

decode(pngData, options?)

| Option | Type | Default | Description | |--------|------|---------|-------------| | raw | boolean | false | If true, return raw unpacked indices/channels without RGBA conversion | | ignoreCrc | boolean | false | Bypass CRC validation for corrupt file recovery | | maxDimensions | number | 16384 | Safety limit on width/height to prevent decompression bomb OOM attacks |

Returns: Promise<{ width, height, data, palette, bitDepth, colorType, interlace, text, dpi, gamma, sRGB }>


Predefined Palettes

| Palette | Colors | Bit Depth | Description | |---------|--------|-----------|-------------| | PALETTES.MASK | 2 | 1 | Transparent + white (default) | | PALETTES.BW | 2 | 1 | Black + white (both opaque) | | PALETTES.GRAYSCALE_4 | 4 | 2 | 4-shade grayscale | | PALETTES.GRAYSCALE_16 | 16 | 4 | 16-shade grayscale | | PALETTES.GRAYSCALE_256 | 256 | 8 | Full 8-bit grayscale | | PALETTES.CGA | 16 | 4 | CGA-style 16-color palette |


License

MIT