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

@radzivon.bartoshyk/pic-scale

v0.7.9

Published

High-performance image resampling

Downloads

620

Readme

@radzivon.bartoshyk/pic-scale

npm

High-performance image resizing for Node.js and the browser, powered by the pic-scale Rust engine with SIMD acceleration (AVX2+FMA on x86-64, NEON on AArch64).

Two delivery modes, one package:

| Mode | Import | When to use | |------------------|--------------------------------------------|------------------------------| | Native addon | require('@radzivon.bartoshyk/pic-scale') | Node.js — fastest, file I/O | | WASM | import ... from '.../pic-scale/wasm' | Browser, Deno, edge runtimes |

Installation

npm install @radzivon.bartoshyk/pic-scale

Native addon (Node.js)

All heavy operations (resize, toBuffer, save) run on Tokio's thread pool and return Promises, so the event loop is never blocked.

import { Image } from '@radzivon.bartoshyk/pic-scale'

// Open from file — metadata (ICC, EXIF orientation) extracted automatically
const img = await Image.open('photo.jpg')
console.log(img.width, img.height, img.channels)

// Resize
const small = await img.resize(800, 600, {
  filter:      'lanczos',   // default
  mode:        'cover',     // fill box, crop from centre
  autoOrient:  true,        // default — bake EXIF rotation into pixels
  withIcc:     true,        // default — copy ICC profile to output
  withExif:    true,        // default — copy EXIF, orientation reset to 1
  withXmp:     false,       // default
  workers:     0,           // 0 = adaptive (all cores)
})

// Save — format inferred from extension, ICC + EXIF injected automatically
await small.save('small.jpg', { quality: 85 })

// Or encode to a Buffer
const buf = await small.toBuffer('webp', { quality: 90 })

// Sync variants — blocks the event loop, useful in scripts or worker threads
const small2 = img.resizeSync(800, 600, { mode: 'fit', bgColor: [0, 0, 0, 255] })
const png    = small2.toBufferSync('png')

Resize modes

| Mode | Description | |----------------|---------------------------------------------------------------------| | "fill" | Stretch to exact (width, height) — ignores aspect ratio (default) | | "cover" | Scale to fill the box, crop excess from the centre | | "fit" | Scale to fit inside the box, pad edges with bgColor | | "fit_width" | Scale to match width, height adjusts proportionally | | "fit_height" | Scale to match height, width adjusts proportionally |

Resampling filters

| Filter | Notes | |---------------|-------------------------------------------------| | nearest | Fastest, blocky | | bilinear | Fast, smooth | | bicubic | Keys cubic | | lanczos | Window-3 sinc — best general quality (default) | | lanczos2 | Faster, slightly softer | | lanczos4 | Slower, very sharp | | box | Area average — best for heavy downscaling | | hamming | | | mitchell | Mitchell-Netravali — balanced sharpness/ringing | | catmull_rom | Sharper than Mitchell | | gaussian | | | hann | |

Metadata

Metadata is extracted automatically when you call Image.open() or Image.fromBuffer() and travels with the image through resize operations.

  • Auto-orient (autoOrient: true, default) — EXIF orientation is baked into pixels before resizing. The orientation tag is reset to 1 in the output so viewers don't rotate twice.
  • ICC profile (withIcc: true, default) — colour profile is injected into the output JPEG, PNG, or WebP.
  • EXIF (withExif: true, default) — EXIF block is injected into the output with the orientation tag reset to 1.
  • XMP (withXmp: false, default) — opt in if you need creative metadata (title, copyright, keywords).

To strip all metadata:

const clean = await img.resize(800, 600, {
  withIcc: false, withExif: false, withXmp: false, autoOrient: true,
})

WASM (browser / Deno / edge)

WASM is single-threaded — all operations are synchronous. For non-blocking behaviour in the browser, run inside a Web Worker:

// worker.js
import init, { Image } from '@radzivon.bartoshyk/pic-scale/wasm'
await init()

self.onmessage = ({ data }) => {
  const img   = Image.fromBytes(data.bytes)
  const small = img.resize(data.width, data.height, 'lanczos', 'cover')
  const out   = small.toBytes('jpeg', 85)
  self.postMessage(out, [out.buffer])   // zero-copy transfer
}
// main.js
const worker = new Worker('./worker.js', { type: 'module' })
const bytes  = new Uint8Array(await (await fetch('photo.jpg')).arrayBuffer())
worker.postMessage({ bytes, width: 800, height: 600 }, [bytes.buffer])
worker.onmessage = ({ data }) => {
  const blob = new Blob([data], { type: 'image/jpeg' })
  document.querySelector('img').src = URL.createObjectURL(blob)
}

WASM API

import init, { Image } from '@radzivon.bartoshyk/pic-scale/wasm'
await init()

const bytes = new Uint8Array(await file.arrayBuffer())
const img   = Image.fromBytes(bytes)         // decode, extract ICC/EXIF
console.log(img.width, img.height, img.channels)

const small = img.resize(
  800, 600,
  'lanczos',  // filter    (default 'lanczos')
  'cover',    // mode      (default 'fill')
  true,       // premultiplyAlpha (default true)
  1,          // workers   (default 1)
  null,       // bgColor   [r,g,b,a] for 'fit' mode
  true,       // autoOrient (default true)
)

const out = small.toBytes(
  'jpeg',     // format  (default 'png')
  85,         // quality (default 85)
  true,       // withIcc  (default true)
  true,       // withExif (default true)
  false,      // withXmp  (default false)
)
// out is a Uint8Array

Supported formats

| Format | Native decode | Native encode | WASM decode | WASM encode | |------------------------|---------------|---------------|-------------|-------------| | JPEG | ✓ | ✓ | ✓ | ✓ | | PNG | ✓ | ✓ | ✓ | ✓ | | WebP | ✓ | ✓ | ✓ | ✓ | | TIFF | ✓ | ✓ | ✓ | ✓ | | BMP | ✓ | ✓ | ✓ | ✓ | | ICO | ✓ | — | ✓ | — | | QOI | ✓ | ✓ | ✓ | ✓ | | AVIF | ✓ | ✓ | — | — | | HEIC/HEIF | ✓ | ✓ | — | — | | JPEG XL (JXL) | ✓ | ✓ | ✓ | ✓ |


Native API reference

Image.open(path): Promise<Image>

Decode from a file path. ICC, EXIF, and XMP are extracted automatically.

Image.fromBuffer(data: Buffer | Uint8Array): Image

Decode from memory (sync).

img.width / img.height / img.channels

Image dimensions and channel count (read-only).

img.resize(width, height, opts?): Promise<Image>

Async resize. Returns a new Image — the original is unchanged.

interface ResizeOptions {
  filter?:          string    // default 'lanczos'
  mode?:            string    // default 'fill'
  premultiplyAlpha?: boolean  // default true
  workers?:         number    // default 1, 0 = adaptive
  bgColor?:         number[]  // [r,g,b,a], default [0,0,0,0]
  withIcc?:         boolean   // default true
  withExif?:        boolean   // default true
  withXmp?:         boolean   // default false
  autoOrient?:      boolean   // default true
}

img.resizeSync(width, height, opts?): Image

Sync resize — same options as resize.

img.toBuffer(format?, opts?): Promise<Buffer>

Encode to a Buffer. Injects ICC + EXIF by default.

img.toBufferSync(format?, opts?): Buffer

Sync encode.

img.save(path, opts?): Promise<void>

Write to a file. Format inferred from extension. Injects ICC + EXIF.

interface EncodeOpts { quality?: number }  // default 85

License

BSD 3-Clause