Skip to content

Fast image compression CLI with watch mode, dry-run previews, plugin support, and more.

License

Notifications You must be signed in to change notification settings

picminjs/picmin

picmin

npm version license node version downloads

Fast image compression CLI with watch mode, dry-run previews, plugin support, and more.

Features

  • Multi-Format Support: JPEG, PNG, WebP, AVIF, GIF, SVG, and TIFF
  • Format Conversion: Convert images to modern formats like AVIF and WebP
  • Compression Options:
    • Lossless Compression: Retains original quality
    • Lossy Compression: Customizable quality levels
    • Mozjpeg Integration: Enhanced JPEG compression (via Sharp)
  • Image Transformation: Resize and crop images during compression
  • Quality Presets: Pre-configured settings (thumbnail, web-optimized, high-quality, print-quality)
  • Glob Pattern Support: Process multiple files with patterns like src/**/*.{jpg,png}
  • Batch Processing: Compress multiple images with worker pool parallelism
  • Watch Mode: Auto-compress images on file changes
  • Dry-Run Mode: Preview what would happen without compressing
  • Config File Support: .picminrc or picmin.config.js
  • Plugin System: Extend functionality with custom plugins
  • Worker Pool: True parallel processing for large batches
  • Configurable Network Speed: Realistic load time estimates
  • Savings Report: Detailed statistics including load time improvements
  • Visual Comparison: Interactive HTML preview with slider to compare quality
  • CLI + API: Use via command-line or integrate into Node.js projects
  • TypeScript Support: Full type definitions included

Installation

For CLI Usage

Install globally:

npm install -g picmin

For Programmatic Use

Install locally:

npm install picmin

CLI Usage

Global Options

  • -v, --verbose: Enable verbose output with detailed processing information
  • -s, --silent: Suppress all output except errors
  • -V, --version: Output the version number
  • -h, --help: Display help for command

Compress a Single Image

picmin compress <input> <output> [options]

Options:

  • -c, --convert-to <format>: Output format (jpeg, png, webp, avif, gif, svg, tiff)
  • -t, --compression-type <type>: Compression type (lossy or lossless)
  • -l, --compression-level <level>: Level (high, medium, low)
  • -q, --quality <quality>: Quality (1-100)
  • --preset <preset>: Quality preset (see below)
  • -p, --generate-preview: Generate interactive HTML comparison
  • --no-mozjpeg: Disable mozjpeg for JPEG
  • --no-strip-metadata: Keep image metadata
  • --no-progressive: Disable progressive JPEG
  • --network-speed <speed>: Network speed for load time estimate (see below)
  • --resize-width <width>: Resize width in pixels
  • --resize-height <height>: Resize height in pixels
  • --resize-fit <fit>: Fit mode (cover, contain, fill, inside, outside)
  • --crop-left/top/width/height: Crop parameters

Network Speed Options:

The --network-speed option accepts:

  • Presets: 2g, 3g, 4g, 5g, slow, average (default), fast, fiber
  • Custom value: Any number in Mbps (e.g., 25 for 25 Mbps)
  • Disable: off to hide load time estimates
Preset Speed (Mbps) Description
2g 0.1 2G mobile
3g 1.5 3G mobile
4g 20 4G LTE
5g 100 5G mobile
slow 1 Slow broadband
average 10 Average broadband (default)
fast 50 Fast broadband
fiber 100 Fiber connection

Examples:

# Convert to AVIF with high quality
picmin compress input.jpg output/ -c avif -q 85

# Use web-optimized preset
picmin compress input.png output/ -c webp --preset web-optimized

# Resize and compress
picmin compress input.jpg output/ -c webp --resize-width 1920 --resize-height 1080

# Generate visual comparison
picmin compress input.jpg output/ -c webp -q 80 -p

# Simulate 3G mobile network
picmin compress input.jpg output/ -c webp --network-speed 3g

# Use custom network speed (25 Mbps)
picmin compress input.jpg output/ -c webp --network-speed 25

# Disable load time estimates
picmin compress input.jpg output/ -c webp --network-speed off

# Verbose mode for debugging
picmin compress input.jpg output/ -c webp -v

# Silent mode (only errors)
picmin compress input.jpg output/ -c webp -s

Batch Compress Images

picmin compress-batch <input> <output> [options]

Input can be:

  • Directory path: ./images
  • Glob pattern: src/**/*.{jpg,png,gif}
  • Comma-separated files: img1.jpg,img2.png

Examples:

# Compress entire directory
picmin compress-batch ./images ./output -c webp --preset web-optimized

# Use glob pattern
picmin compress-batch "src/**/*.{jpg,png}" dist/images/ -c avif -q 80

# Process with higher concurrency
picmin compress-batch ./images ./output -c webp -y 10

# Use parallel worker pool for large batches (recommended for 10+ images)
picmin compress-batch ./images ./output -c webp --parallel

# Specify worker count (default: CPU cores - 1)
picmin compress-batch ./images ./output -c webp --parallel --workers 4

# Silent batch processing (great for CI/CD)
picmin compress-batch ./images ./output -c webp -s

View Quality Presets

picmin presets

Watch Mode

Auto-compress images when files change:

# Watch a directory and compress changes to WebP
picmin watch ./src/images ./dist/images -c webp

# Watch with custom quality
picmin watch ./images ./output -c avif -q 85

# With debounce delay (default: 300ms)
picmin watch ./images ./output -c webp --debounce 500

Dry-Run Mode

Preview compression results without actually processing:

# See what would happen
picmin dry-run ./images ./output -c webp

# Verbose output with dimensions and load times
picmin dry-run ./images ./output -c webp -v

# Alias: preview
picmin preview ./images ./output -c avif -q 80

Configuration File

Create a config file for consistent settings:

# Create picmin.config.js
picmin init

# Create .picminrc (JSON format)
picmin init --format json

The CLI automatically loads config from (in order):

  • .picminrc
  • .picminrc.json
  • .picminrc.js
  • picmin.config.js

Example picmin.config.js:

module.exports = {
  convertTo: 'webp',
  quality: 80,
  compressionType: 'lossy',
  useMozjpeg: true,
  networkSpeed: 'fast',
  
  // Watch mode settings
  watch: {
    ignored: ['**/node_modules/**', '**/output/**'],
    debounceDelay: 300
  },
  
  // Plugins
  plugins: [
    // ['plugin-name', { options }]
  ]
};

Quality Presets

Preset Quality Use Case
thumbnail 60 Small thumbnails (auto-resizes to 300x300)
web-optimized 80 Balanced for web (recommended)
high-quality 92 High quality with moderate compression
print-quality 95 Near-lossless for print

Programmatic API Usage

TypeScript/JavaScript

import { compressImage, compressImages, QUALITY_PRESETS } from 'picmin';

// Compress single image with AVIF
const stats = await compressImage('input.jpg', 'output/', {
  convertTo: 'avif',
  quality: 80,
  generatePreview: true
});

console.log(`Saved ${stats.savings}% • ${stats.loadTimeImprovement}s faster`);

// Resize during compression
await compressImage('large.jpg', 'output/', {
  convertTo: 'webp',
  preset: 'web-optimized',
  resize: {
    width: 1920,
    height: 1080,
    fit: 'inside'
  }
});

// Batch compress with glob pattern
const results = await compressImages('src/images/**/*.{jpg,png}', 'dist/', {
  convertTo: 'webp',
  quality: 85,
  concurrency: 10,
  useMozjpeg: true
});

console.log(`Compressed ${results.length} images`);

// Use worker pool for faster parallel processing
const parallelResults = await compressImages('./images', './output', {
  convertTo: 'avif',
  quality: 80,
  useWorkerPool: true,     // Enable worker threads
  workerPoolSize: 4        // Optional: specify worker count
});

// Crop and compress
await compressImage('input.jpg', 'output/', {
  convertTo: 'jpeg',
  crop: {
    left: 100,
    top: 100,
    width: 800,
    height: 600
  },
  quality: 90
});

Advanced Options

interface CompressionOptions {
  convertTo: 'jpeg' | 'png' | 'webp' | 'avif' | 'gif' | 'svg' | 'tiff';
  quality?: number;                    // 1-100
  compressionLevel?: 'high' | 'medium' | 'low';
  compressionType?: 'lossy' | 'lossless';
  preset?: 'thumbnail' | 'web-optimized' | 'high-quality' | 'print-quality';
  resize?: {
    width?: number;
    height?: number;
    fit?: 'cover' | 'contain' | 'fill' | 'inside' | 'outside';
  };
  crop?: {
    left: number;
    top: number;
    width: number;
    height: number;
  };
  generatePreview?: boolean;
  useMozjpeg?: boolean;               // Default: true for JPEG
  stripMetadata?: boolean;            // Default: true
  progressive?: boolean;              // Default: true for JPEG
  networkSpeed?: number | string;     // Mbps or preset name
  concurrency?: number;               // Batch only, default: 5
  useWorkerPool?: boolean;            // Batch only, enables true parallel processing
  workerPoolSize?: number;            // Worker threads, default: CPU cores - 1
}

Watch Mode API

import { watchDirectory } from 'picmin';

const watcher = watchDirectory('./src/images', './dist/images', {
  convertTo: 'webp',
  quality: 80,
  watch: {
    ignored: ['**/temp/**'],
    debounceDelay: 500
  }
}, {
  onReady: (dir) => console.log(`Watching ${dir}`),
  onChange: (file) => console.log(`Processing ${file}`),
  onProcessed: (file, result) => console.log(`Done: ${result.savings}% saved`),
  onError: (file, err) => console.error(`Error: ${err.message}`)
});

// Stop watching
watcher.close();

Dry-Run API

import { analyzeFiles, formatDryRunOutput } from 'picmin';

const analysis = await analyzeFiles('./images', './output', {
  convertTo: 'webp',
  quality: 80
});

console.log(formatDryRunOutput(analysis, true)); // verbose

// Or access data directly
console.log(`Would save ~${analysis.summary.totalEstimatedSavings}%`);
console.log(`Files to process: ${analysis.summary.analyzedFiles}`);

Plugin System

import { createPlugin, pluginManager } from 'picmin';

// Create a custom plugin
const myPlugin = createPlugin('my-plugin', {
  beforeCompress: async (context, options) => {
    console.log(`About to compress: ${context.inputPath}`);
    return context; // Can modify context
  },
  afterCompress: async (context, options) => {
    console.log(`Compressed: ${context.stats.savings}% saved`);
  }
});

// Load plugins
pluginManager.load(myPlugin);

// Or load from config
pluginManager.loadAll([
  ['./my-plugin.js', { option: 'value' }],
  'another-plugin'
]);

Output Statistics

Each compression returns detailed statistics:

{
  inputSize: number;           // Original size in bytes
  outputSize: number;          // Compressed size in bytes
  savings: string;             // Percentage reduction
  originalLoadTime: string;    // Estimated load time (original)
  compressedLoadTime: string;  // Estimated load time (compressed)
  loadTimeImprovement: string; // Time saved in seconds
  inputFileName: string;       // Filename
}

Visual Comparison Feature

The --generate-preview or generatePreview: true option creates an interactive HTML file with a draggable slider to compare original vs compressed images side-by-side.

This feature helps you:

  • Visually assess compression quality
  • Make informed decisions about quality settings
  • Share comparisons with stakeholders

Key Features

This package combines several useful features in one tool:

Feature Included
AVIF, WebP, GIF, SVG Support
Mozjpeg Integration
Interactive Visual Comparison
Glob Pattern Support
Quality Presets
Resize/Crop
TypeScript Definitions
CLI + Programmatic API
Load Time Statistics

Examples

Check out the examples directory for more use cases:

node examples/example.js

Supported Formats

Format Read Write Lossy Lossless Notes
JPEG Mozjpeg support
PNG
WebP
AVIF Modern format
GIF Gifsicle integration
SVG SVGO optimization
TIFF

Benchmarks

Tested on a real 810x1440 JPEG photo (275 KB), averaged over multiple runs:

Format picmin raw sharp Overhead
JPEG 114 ms 114 ms ~0%
WebP 125 ms 121 ms ~3%
AVIF 1.94 s 1.68 s ~15%

Batch Processing (10 images):

Mode Time Notes
Sequential 664 ms Single-threaded
Parallel (worker pool) 717 ms Multi-threaded

Worker pool overhead is noticeable with small images. Benefits increase with larger images or CPU-intensive formats like AVIF.

Compression Results (quality: 80):

Output Format Size Savings
JPEG 129 KB 53% smaller
WebP 105 KB 62% smaller
AVIF 154 KB 44% smaller

Note: picmin adds minimal overhead (~3-15%) for features like presets, metadata handling, preview generation, and statistics. For raw speed, use sharp directly. For convenience and features, use picmin.

Run benchmarks yourself:

node benchmarks/benchmark.js

Performance Tips

  1. Use AVIF or WebP for modern format compression (typically smaller than JPEG)
  2. Enable mozjpeg for improved JPEG compression (default: enabled)
  3. Adjust concurrency based on your CPU cores
  4. Use glob patterns for efficient batch processing
  5. Apply quality presets for consistent results

Contributing

Contributions are welcome! Please read our contributing guidelines before submitting a pull request.

This project follows the Contributor Covenant Code of Conduct.

Changelog

See changelog.md for version history and updates.

License

This project is licensed under the MIT License. See the license file for details.

Support

Acknowledgments

Built with:

  • Sharp - High performance image processing (includes mozjpeg)
  • SVGO - SVG optimization
  • Commander - CLI framework

npmGitHubIssues

About

Fast image compression CLI with watch mode, dry-run previews, plugin support, and more.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks