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
Maintainers
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, andsRGBmetadata. - Adam7 Interlacing: Progressive 7-pass interlacing support for encoders and decoders.
- Fault-Tolerant & Secure:
ignoreCrcmode for corrupt PNG recovery +maxDimensionsdecompression bomb protection. - Zero Browser Dependencies: Uses native
CompressionStream/DecompressionStream. - Node.js 18+ Support: Native streams with optional
pakopeer dependency fallback.
Installation
npm install pngcraftFor legacy Node.js environments (<18) or older browsers without Compression Streams API:
npm install pngcraft pakoQuick 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
