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

pyjamaz

v1.0.1

Published

Node.js bindings for Pyjamaz - high-performance image optimizer

Readme

Pyjamaz Node.js Bindings

High-performance image optimizer with perceptual quality guarantees for Node.js.

TypeScript-first Node.js bindings for Pyjamaz - a blazing-fast CLI image optimizer built with Zig.

Features

  • 🚀 Blazing Fast: 50-100ms per image with parallel encoding
  • 💾 Intelligent Caching: 15-20x speedup on repeated optimizations
  • 🎯 Smart Optimization: Automatic format selection (JPEG/PNG/WebP/AVIF)
  • 📊 Perceptual Quality: DSSIM & SSIMULACRA2 metrics
  • 🔒 Size Guarantees: Never exceed maxBytes constraint
  • 🧹 Auto Memory Management: No manual cleanup required
  • 📘 TypeScript-First: Full type safety with IntelliSense support
  • 🐍 Dual API: Both sync and async variants

Installation

From Source (Development)

# Clone the repository
git clone https://github.com/yourusername/pyjamaz.git
cd pyjamaz

# Build the shared library
zig build

# Install Node.js bindings
cd bindings/nodejs
npm install
npm run build

From npm (Coming Soon)

npm install @pyjamaz/nodejs

Quick Start

TypeScript

import * as pyjamaz from '@pyjamaz/nodejs';

// Optimize with size constraint
const result = await pyjamaz.optimizeImage('input.jpg', {
  maxBytes: 100_000,
});

if (result.passed) {
  await result.save('output.jpg');
  console.log(`Optimized to ${result.size} bytes`);
}

JavaScript

const pyjamaz = require('@pyjamaz/nodejs');

// Optimize with size constraint
const result = await pyjamaz.optimizeImage('input.jpg', {
  maxBytes: 100000,
});

if (result.passed) {
  await result.save('output.jpg');
  console.log(`Optimized to ${result.size} bytes`);
}

API Reference

Core Functions

optimizeImage(inputPath, options?)

Optimize an image from file path (async).

async function optimizeImage(
  inputPath: string,
  options?: OptimizeOptions
): Promise<OptimizeResult>

Example:

const result = await pyjamaz.optimizeImage('input.jpg', {
  maxBytes: 100_000,
  metric: 'dssim',
});

optimizeImageSync(inputPath, options?)

Optimize an image from file path (sync).

function optimizeImageSync(
  inputPath: string,
  options?: OptimizeOptions
): OptimizeResult

Example:

const result = pyjamaz.optimizeImageSync('input.jpg', {
  maxBytes: 100_000,
});

optimizeImageFromBuffer(buffer, options?)

Optimize an image from Buffer (async).

async function optimizeImageFromBuffer(
  buffer: Buffer,
  options?: OptimizeOptions
): Promise<OptimizeResult>

Example:

const inputData = await fs.promises.readFile('input.jpg');
const result = await pyjamaz.optimizeImageFromBuffer(inputData, {
  maxBytes: 100_000,
});

getVersion()

Get the Pyjamaz library version.

function getVersion(): string

Example:

console.log(`Pyjamaz version: ${pyjamaz.getVersion()}`);

OptimizeOptions

interface OptimizeOptions {
  maxBytes?: number;           // Max output size (undefined = no limit)
  maxDiff?: number;            // Max perceptual difference
  metric?: MetricType;         // 'dssim' | 'ssimulacra2' | 'none'
  formats?: ImageFormat[];     // ['jpeg', 'png', 'webp', 'avif']
  concurrency?: number;        // Parallel threads (1-8, default 4)
  cacheEnabled?: boolean;      // Enable caching (default true)
  cacheDir?: string;           // Custom cache directory
  cacheMaxSize?: number;       // Max cache size in bytes (default 1GB)
}

OptimizeResult

interface OptimizeResult {
  data: Buffer;                // Optimized image data
  format: ImageFormat;         // Selected format
  diffValue: number;           // Perceptual difference score
  passed: boolean;             // Whether constraints met
  errorMessage?: string;       // Error message if failed
  readonly size: number;       // Size in bytes

  save(path: string): Promise<void>;     // Save async
  saveSync(path: string): void;          // Save sync
}

Usage Examples

Basic Optimization

// Size constraint
const result = await pyjamaz.optimizeImage('input.jpg', {
  maxBytes: 100_000,
});

// Quality constraint
const result = await pyjamaz.optimizeImage('input.png', {
  maxDiff: 0.002,
  metric: 'ssimulacra2',
});

// Dual constraints
const result = await pyjamaz.optimizeImage('input.jpg', {
  maxBytes: 80_000,
  maxDiff: 0.001,
  metric: 'dssim',
});

Format Selection

// Try all formats (default)
const result = await pyjamaz.optimizeImage('input.jpg', {
  maxBytes: 100_000,
});
console.log(`Best format: ${result.format}`);

// Modern formats only
const result = await pyjamaz.optimizeImage('input.jpg', {
  formats: ['webp', 'avif'],
  maxBytes: 50_000,
});

From Buffer

// Optimize from memory
const inputData = await fs.promises.readFile('input.jpg');
const result = await pyjamaz.optimizeImageFromBuffer(inputData, {
  maxBytes: 100_000,
});

// Save result
await fs.promises.writeFile('output.webp', result.data);
// or
await result.save('output.webp');

Batch Processing

import * as fs from 'fs';
import * as path from 'path';

async function optimizeBatch(
  inputDir: string,
  outputDir: string,
  maxBytes: number
): Promise<void> {
  const files = await fs.promises.readdir(inputDir);
  const images = files.filter(f => /\.(jpg|png|webp)$/i.test(f));

  // Process in parallel
  const promises = images.map(async (filename) => {
    const inputPath = path.join(inputDir, filename);
    const result = await pyjamaz.optimizeImage(inputPath, {
      maxBytes,
      cacheEnabled: true,
    });

    if (result.passed) {
      const outputPath = path.join(
        outputDir,
        `${path.parse(filename).name}.${result.format}`
      );
      await result.save(outputPath);
      console.log(`✓ ${filename}: ${result.size} bytes`);
    } else {
      console.log(`✗ ${filename}: ${result.errorMessage}`);
    }
  });

  await Promise.all(promises);
}

// Usage
await optimizeBatch('images', 'optimized', 100_000);

Caching

// Enable caching (default)
const result = await pyjamaz.optimizeImage('input.jpg', {
  maxBytes: 100_000,
  cacheEnabled: true,
});

// Disable caching
const result = await pyjamaz.optimizeImage('input.jpg', {
  maxBytes: 100_000,
  cacheEnabled: false,
});

// Custom cache settings
const result = await pyjamaz.optimizeImage('input.jpg', {
  cacheEnabled: true,
  cacheDir: '/tmp/my-cache',
  cacheMaxSize: 2 * 1024 * 1024 * 1024, // 2GB
});

// Measure cache speedup
const start1 = Date.now();
const result1 = await pyjamaz.optimizeImage('input.jpg', { maxBytes: 100_000 });
const time1 = Date.now() - start1;

const start2 = Date.now();
const result2 = await pyjamaz.optimizeImage('input.jpg', { maxBytes: 100_000 });
const time2 = Date.now() - start2;

console.log(`First run: ${time1}ms`);
console.log(`Second run: ${time2}ms (${(time1 / time2).toFixed(1)}x faster)`);

Integration Examples

Express Server

import express from 'express';
import multer from 'multer';
import * as pyjamaz from '@pyjamaz/nodejs';

const app = express();
const upload = multer();

app.post('/optimize', upload.single('image'), async (req, res) => {
  try {
    if (!req.file) {
      return res.status(400).json({ error: 'No image provided' });
    }

    const result = await pyjamaz.optimizeImageFromBuffer(req.file.buffer, {
      maxBytes: 100_000,
      metric: 'ssimulacra2',
    });

    if (!result.passed) {
      return res.status(400).json({ error: result.errorMessage });
    }

    res.set('Content-Type', `image/${result.format}`);
    res.send(result.data);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.listen(3000);

Fastify Server

import Fastify from 'fastify';
import multipart from '@fastify/multipart';
import * as pyjamaz from '@pyjamaz/nodejs';

const fastify = Fastify();
await fastify.register(multipart);

fastify.post('/optimize', async (request, reply) => {
  const data = await request.file();
  const buffer = await data.toBuffer();

  const result = await pyjamaz.optimizeImageFromBuffer(buffer, {
    maxBytes: 100_000,
  });

  if (!result.passed) {
    return reply.code(400).send({ error: result.errorMessage });
  }

  reply.type(`image/${result.format}`).send(result.data);
});

await fastify.listen({ port: 3000 });

Development

Running Tests

# Install dependencies
npm install

# Build TypeScript
npm run build

# Run all tests
npm test

# Run TypeScript tests only
npm run test:ts

# Run JavaScript tests only
npm run test:js

# With coverage
npm run test:coverage

Code Quality

# Format code
npm run format

# Lint
npm run lint

# Type checking
tsc --noEmit

Performance

Platform: Apple M1 Pro, macOS 15.0

| Operation | Time | Notes | |-----------|------|-------| | Optimize (first run) | 50-100ms | Full optimization | | Optimize (cache hit) | 5-10ms | 15-20x faster | | Batch (100 images, parallel) | ~3s | 4 workers |

Requirements

  • Node.js: 14.0.0 or higher
  • TypeScript: 5.0+ (for development)
  • libpyjamaz: Shared library (built from Zig source)
  • System dependencies: libvips, libjpeg-turbo, libdssim

Troubleshooting

Library Not Found

If you get Could not find libpyjamaz shared library:

  1. Build the shared library:

    cd /path/to/pyjamaz
    zig build
  2. Set PYJAMAZ_LIB_PATH:

    export PYJAMAZ_LIB_PATH=/path/to/pyjamaz/zig-out/lib/libpyjamaz.dylib
  3. Or install system-wide:

    sudo cp zig-out/lib/libpyjamaz.* /usr/local/lib/

Import Errors

Make sure you've built the TypeScript code:

npm run build

Then use correct import:

// TypeScript/ESM
import * as pyjamaz from '@pyjamaz/nodejs';

// CommonJS
const pyjamaz = require('@pyjamaz/nodejs');

License

MIT License - see LICENSE for details.

Contributing

Contributions welcome! See CONTRIBUTING.md for guidelines.

Links