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

@pictomancer/node

v0.5.0

Published

Node.js SDK for Pictomancer.ai - thin client for the image optimization API at api.pictomancer.ai

Downloads

636

Readme

@pictomancer/node

Node.js SDK for Pictomancer.ai - a thin, zero-dependency client for the REST API at https://api.pictomancer.ai. Uses native fetch (Node >= 18).

Install

npm install @pictomancer/node

Configuration

  • apiKey - optional Bearer token (Authorization: Bearer ...).
  • baseUrl - defaults to https://api.pictomancer.ai.
  • timeoutMs - request timeout in milliseconds (default 30000).
  • agentWallet - optional wallet identity sent as X-Agent-Wallet (x402 tracking).

JSON helpers return typed objects; image operations return Uint8Array (inline delivery) or a JSON receipt (put_url/callback delivery).

Usage

import { Client } from "@pictomancer/node";

const client = new Client({ apiKey: "your-api-key" });

const info = await client.info();
const usage = await client.usage();

const meta = await client.analyze("https://example.com/image.jpg");

let out = await client.resize("https://example.com/image.jpg", { scale: 0.5, format: "webp" });
out = await client.compress("https://example.com/image.jpg", { q: 85, format: "jpeg" });
out = await client.convert("https://example.com/image.jpg", "avif", { q: 50, effort: 2 });
out = await client.crop("https://example.com/image.jpg", 0, 0, 100, 100, { format: "webp" });
out = await client.pipeline("https://example.com/image.jpg", [
  { type: "resize", params: { scale: "0.5" } },
  { type: "convert", params: { format: "webp" } },
]);

import { writeFile } from "node:fs/promises";
await writeFile("out.webp", out as Uint8Array);

Sources can be an image URL, a base64 string, or a data: URI. For local files or in-memory bytes:

import { Source } from "@pictomancer/node";

const out = await client.compress(await Source.fromFile("photo.jpg"), { q: 80 });
const out2 = await client.compress(Source.fromBytes(bytes), { q: 80 });

Geometry ops: smart crop, trim, fill, autorot

crop has three mutually exclusive modes. Pass null for the positional x/y/width/height args not used by a given mode.

// Manual: exact rectangle.
let out = await client.crop("https://example.com/image.jpg", 0, 0, 100, 100);

// Smart: gravity picks the window. One of "attention" | "entropy" | "centre".
out = await client.crop("https://example.com/image.jpg", null, null, 200, 200, {
  gravity: "attention",
});

// Trim: removes a uniform background border. threshold defaults to 10.0 server-side.
out = await client.crop("https://example.com/image.jpg", null, null, null, null, {
  trim: true,
  threshold: 5,
});

resize gains a fill mode: pass width + height (instead of scale/scale_x/scale_y) to resize and smart-crop to exact dimensions in one call; gravity defaults to "attention".

const out = await client.resize("https://example.com/image.jpg", {
  width: 200,
  height: 150,
  gravity: "entropy",
});

All four ops (resize, compress, convert, crop) accept autorot: true to apply EXIF orientation before processing.

When a crop actually trims, the response carries X-Pictomancer-Trim-Left/-Top/-Width/-Height headers.

Enhance: denoise, auto-contrast, sharpen

All four ops (resize, compress, convert, crop) accept three opt-in modifiers, applied in order autorot -> denoise -> equalize -> op -> sharpen. Base price, no surcharge.

const out = await client.compress("https://example.com/image.jpg", {
  format: "webp",
  denoise: 2,
  equalize: true,
  sharpen: true,
});
  • denoise - median denoise radius 1-3 (window 3x3 to 7x7) before the operation.
  • equalize - auto-contrast: histogram equalisation of the value channel, hue and saturation preserved.
  • sharpen - unsharp-mask sharpen after the operation (libvips defaults).

A compress with any of the three that ends up larger than the input IS billed (X-Pig-Billed: 1), unlike a plain compress with no gain.

Perceptual quality target

Instead of guessing a q value, ask for the smallest file that keeps SSIM at or above a target. Supported on compress and convert for jpeg, webp and avif. Mutually exclusive with q (and with lossless on convert); compress requires an explicit format. Not available inside pipelines. The server rejects invalid combinations with a 422.

const out = await client.compress("https://example.com/image.jpg", {
  format: "webp",
  quality_target: 0.95,
});

const avif = await client.convert("https://example.com/image.jpg", "avif", {
  quality_target: 0.9,
});

The API reports the search outcome in response headers: X-Pictomancer-Quality-Target, X-Pictomancer-Quality-Achieved, X-Pictomancer-Quality-Q-Final and X-Pictomancer-Quality-Encodes (absent when no search ran). X-Pig-Billed is 0 when the input already met the target and came back untouched. The SDK returns the body only (bytes or receipt) and does not surface response headers.

AI-generated images: one call to web-ready

Image generators (gpt-image, DALL-E, Flux, Midjourney, Stable Diffusion) return 2-8 MB PNGs. optimize_generated returns the same picture as web-ready webp (default), avif, jpeg or png: metadata stripped, transparency kept, optional max_dimension cap (never upscales), optional q or quality_target. Same price as convert; a result that is not smaller is returned free.

const out = await client.optimizeGenerated("https://example.com/gen.png", { format: "avif", max_dimension: 1600 });

Delivery targets

By default the optimized bytes come back inline. For large or async jobs, deliver straight to your storage or endpoint instead - the op then returns a JSON receipt:

import { Client, Delivery } from "@pictomancer/node";

const client = new Client({ apiKey: "your-api-key" });

// Presigned PUT (S3/R2/GCS/Azure). No cloud credentials reach Pictomancer.
const receipt = await client.compress("https://example.com/big.png", {
  format: "webp",
  delivery: Delivery.putUrl(presignedUrl),
});

// POST to your endpoint, HMAC-signed (X-Pig-Signature: sha256=<hex>).
await client.convert("https://example.com/big.png", "avif", {
  delivery: Delivery.callback("https://hooks.example.com/pig?token=...", {
    secret: process.env.PIG_WEBHOOK_SECRET,
  }),
});

Errors

Non-2xx responses throw PictomancerError with status and detail:

import { PictomancerError } from "@pictomancer/node";

try {
  await client.resize("https://example.com/image.jpg", { scale: 0.5 });
} catch (e) {
  if (e instanceof PictomancerError && e.status === 402) {
    // free tier exhausted: pay per request (x402) or use an API key
  }
}

Development

npm install
npm test
npm run typecheck
npm run build

License

MIT