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

image-compressor-by-xettri-aleen

v1.0.2

Published

Smart image compression with automatic CSP fallbacks and modern browser support

Readme

🗜️ image-compressor-by-xettri-aleen

npm version License: MIT Bundle Size TypeScript CSP Safe

Smart, developer-friendly image compression with automatic CSP fallbacks and multiple compression strategies.

image-compressor-by-xettri-aleen automatically detects your browser's capabilities and chooses the best compression method available. Works everywhere from strict CSP environments to modern browsers with full canvas access.

✨ Features

  • 🚀 Multiple Compression Methods - Canvas, Web Workers, WebAssembly, and lightweight fallbacks
  • 🛡️ CSP-Safe - Automatically handles Content Security Policy restrictions
  • 📱 Universal Browser Support - Works in all modern browsers and environments
  • High Performance - Uses the fastest method available in your environment
  • 🎨 Multiple Formats - WebP, JPEG, PNG support with format conversion
  • 📏 Smart Resizing - Maintains aspect ratio while respecting size constraints
  • 📊 Detailed Logging - Optional compression statistics and performance metrics
  • 🔧 TypeScript Support - Full type definitions included
  • 📦 Multiple Build Formats - ES modules, UMD, and standalone builds
  • 🎯 Zero Dependencies - Standalone build works without external libraries
  • 🔄 Batch Processing - Compress multiple images efficiently
  • 🎛️ Quality Presets - Pre-configured settings for common use cases

📦 Installation

NPM Installation

npm install image-compressor-by-xettri-aleen

Yarn Installation

bash

yarn add image-compressor-by-xettri-aleen

CDN Usage

html

<!-- Standalone build (no dependencies) -->
<script src="https://unpkg.com/image-compressor-by-xettri-aleen/dist/index.standalone.js"></script>

<!-- UMD build (requires browser-image-compression for WASM features) -->
<script src="https://unpkg.com/browser-image-compression/dist/browser-image-compression.js"></script>
<script src="https://unpkg.com/image-compressor-by-xettri-aleen/dist/index.js"></script>

<!-- ES Module via CDN -->
<script type="module">
  import { compressImage } from 'https://unpkg.com/image-compressor-by-xettri-aleen/dist/index.esm.js';
</script>

🚀 Quick Start

Basic Usage (ES Modules)

javascript

import { compressImage, QUALITY_PRESETS } from 'image-compressor-by-xettri-aleen';

// Basic compression
const fileInput = document.querySelector('input[type="file"]');
fileInput.addEventListener('change', async (e) => {
  const file = e.target.files[0];

  const result = await compressImage(file, {
    quality: QUALITY_PRESETS.MEDIUM,
    format: 'webp',
    enableLogging: true
  });

  console.log(`Reduced size by ${result.stats.compressionRatio}%`);
  console.log(`Original: ${formatBytes(result.stats.originalSize)}`);
  console.log(`Compressed: ${formatBytes(result.stats.compressedSize)}`);

  // Download compressed image
  const url = URL.createObjectURL(result.blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = result.file.name;
  a.click();
  URL.revokeObjectURL(url);
});

function formatBytes(bytes) {
  if (bytes === 0) return '0 Bytes';
  const k = 1024;
  const sizes = ['Bytes', 'KB', 'MB', 'GB'];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
}

Browser Global Usage

html

<!DOCTYPE html>
<html>
<head>
    <title>Image Compression Demo</title>
</head>
<body>
    <input type="file" id="imageInput" accept="image/*">
    <div id="results"></div>

    <script src="https://unpkg.com/image-compressor-by-xettri-aleen/dist/index.standalone.js"></script>
    <script>
        document.getElementById('imageInput').addEventListener('change', async (e) => {
            const file = e.target.files[0];
            if (!file) return;

            try {
                const result = await ImageSquash.compress(file, {
                    quality: ImageSquash.presets.HIGH,
                    format: 'webp',
                    maxWidth: 1920,
                    enableLogging: true
                });

                document.getElementById('results').innerHTML = `
                    <h3>Compression Results</h3>
                    <p>Original Size: ${formatBytes(result.stats.originalSize)}</p>
                    <p>Compressed Size: ${formatBytes(result.stats.compressedSize)}</p>
                    <p>Reduction: ${result.stats.compressionRatio}%</p>
                    <p>Method Used: ${result.method}</p>
                `;

            } catch (error) {
                console.error('Compression failed:', error);
            }
        });

        function formatBytes(bytes) {
            if (bytes === 0) return '0 Bytes';
            const k = 1024;
            const sizes = ['Bytes', 'KB', 'MB', 'GB'];
            const i = Math.floor(Math.log(bytes) / Math.log(k));
            return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
        }
    </script>
</body>
</html>

React Example

jsx

import React, { useState, useCallback } from 'react';
import { compressImage, QUALITY_PRESETS, FORMAT_OPTIONS } from 'image-compressor-by-xettri-aleen';

function ImageCompressor() {
  const [result, setResult] = useState(null);
  const [loading, setLoading] = useState(false);
  const [progress, setProgress] = useState(0);

  const handleFile = useCallback(async (e) => {
    const file = e.target.files[0];
    if (!file) return;

    setLoading(true);
    setProgress(0);

    try {
      const compressed = await compressImage(file, {
        quality: QUALITY_PRESETS.MEDIUM,
        format: FORMAT_OPTIONS.WEBP,
        maxWidth: 1920,
        maxHeight: 1080,
        enableLogging: true,
        logLevel: 'detailed'
      });

      setResult(compressed);
      setProgress(100);

    } catch (error) {
      console.error('Compression failed:', error);
      alert('Compression failed: ' + error.message);
    }

    setLoading(false);
  }, []);

  const downloadImage = useCallback(() => {
    if (!result) return;

    const url = URL.createObjectURL(result.blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = result.file.name;
    a.click();
    URL.revokeObjectURL(url);
  }, [result]);

  return (
    <div style={{ padding: '20px', maxWidth: '600px', margin: '0 auto' }}>
      <h2>🗜️ Image Compressor</h2>

      <input
        type="file"
        accept="image/*"
        onChange={handleFile}
        disabled={loading}
        style={{ marginBottom: '20px' }}
      />

      {loading && (
        <div style={{ marginBottom: '20px' }}>
          <p>Compressing image...</p>
          <div style={{
            width: '100%',
            height: '20px',
            backgroundColor: '#f0f0f0',
            borderRadius: '10px',
            overflow: 'hidden'
          }}>
            <div style={{
              width: `${progress}%`,
              height: '100%',
              backgroundColor: '#007bff',
              transition: 'width 0.3s ease'
            }} />
          </div>
        </div>
      )}

      {result && (
        <div style={{
          padding: '15px',
          border: '1px solid #ddd',
          borderRadius: '5px',
          backgroundColor: '#f9f9f9'
        }}>
          <h3>✅ Compression Complete!</h3>
          <ul>
            <li><strong>Method Used:</strong> {result.method}</li>
            <li><strong>Original Size:</strong> {formatBytes(result.stats.originalSize)}</li>
            <li><strong>Compressed Size:</strong> {formatBytes(result.stats.compressedSize)}</li>
            <li><strong>Space Saved:</strong> {formatBytes(result.stats.savedBytes)} ({result.stats.compressionRatio}%)</li>
            <li><strong>Output Format:</strong> {result.stats.format}</li>
          </ul>

          <button
            onClick={downloadImage}
            style={{
              padding: '10px 20px',
              backgroundColor: '#007bff',
              color: 'white',
              border: 'none',
              borderRadius: '5px',
              cursor: 'pointer'
            }}
          >
            📥 Download Compressed Image
          </button>

          <div style={{ marginTop: '15px' }}>
            <img
              src={URL.createObjectURL(result.blob)}
              alt="Compressed"
              style={{ maxWidth: '100%', height: 'auto', borderRadius: '5px' }}
            />
          </div>
        </div>
      )}
    </div>
  );
}

function formatBytes(bytes) {
  if (bytes === 0) return '0 Bytes';
  const k = 1024;
  const sizes = ['Bytes', 'KB', 'MB', 'GB'];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
}

export default ImageCompressor;

Vue.js Example

vue

<template>
  <div class="image-compressor">
    <h2>🗜️ Image Compressor</h2>

    <input
      type="file"
      accept="image/*"
      @change="handleFile"
      :disabled="loading"
      ref="fileInput"
    />

    <div v-if="loading" class="loading">
      <p>Compressing image...</p>
      <div class="progress-bar">
        <div class="progress-fill" :style="{ width: progress + '%' }"></div>
      </div>
    </div>

    <div v-if="result" class="results">
      <h3>✅ Compression Complete!</h3>
      <ul>
        <li><strong>Method:</strong> {{ result.method }}</li>
        <li><strong>Original:</strong> {{ formatBytes(result.stats.originalSize) }}</li>
        <li><strong>Compressed:</strong> {{ formatBytes(result.stats.compressedSize) }}</li>
        <li><strong>Saved:</strong> {{ result.stats.compressionRatio }}%</li>
      </ul>

      <button @click="downloadImage">📥 Download</button>

      <img
        :src="imageUrl"
        alt="Compressed"
        class="preview"
      />
    </div>
  </div>
</template>

<script>
import { compressImage, QUALITY_PRESETS } from 'image-compressor-by-xettri-aleen';

export default {
  name: 'ImageCompressor',
  data() {
    return {
      result: null,
      loading: false,
      progress: 0,
      imageUrl: null
    };
  },
  methods: {
    async handleFile(event) {
      const file = event.target.files[0];
      if (!file) return;

      this.loading = true;
      this.progress = 0;

      try {
        this.result = await compressImage(file, {
          quality: QUALITY_PRESETS.MEDIUM,
          format: 'webp',
          maxWidth: 1920,
          enableLogging: true
        });

        this.imageUrl = URL.createObjectURL(this.result.blob);
        this.progress = 100;

      } catch (error) {
        console.error('Compression failed:', error);
        alert('Compression failed: ' + error.message);
      }

      this.loading = false;
    },

    downloadImage() {
      if (!this.result) return;

      const a = document.createElement('a');
      a.href = this.imageUrl;
      a.download = this.result.file.name;
      a.click();
    },

    formatBytes(bytes) {
      if (bytes === 0) return '0 Bytes';
      const k = 1024;
      const sizes = ['Bytes', 'KB', 'MB', 'GB'];
      const i = Math.floor(Math.log(bytes) / Math.log(k));
      return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
    }
  },

  beforeUnmount() {
    if (this.imageUrl) {
      URL.revokeObjectURL(this.imageUrl);
    }
  }
};
</script>

<style scoped>
.image-compressor {
  max-width: 600px;
  margin: 0 auto;
  padding: 20px;
}

.loading {
  margin: 20px 0;
}

.progress-bar {
  width: 100%;
  height: 20px;
  background: #f0f0f0;
  border-radius: 10px;
  overflow: hidden;
}

.progress-fill {
  height: 100%;
  background: #007bff;
  transition: width 0.3s ease;
}

.results {
  margin-top: 20px;
  padding: 15px;
  border: 1px solid #ddd;
  border-radius: 5px;
  background: #f9f9f9;
}

.preview {
  max-width: 100%;
  height: auto;
  margin-top: 15px;
  border-radius: 5px;
}

button {
  padding: 10px 20px;
  background: #007bff;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  margin: 10px 0;
}

button:hover {
  background: #0056b3;
}
</style>

📖 API Reference

compressImage(input, options)

Main compression function that automatically selects the best method.

Parameters:

  • input - File, Blob, HTMLImageElement, or data URL string
  • options - Compression configuration object

Returns: Promise resolving to compression result object

Options:

typescript

interface CompressionOptions {
  quality?: number;              // 0-1, compression quality (default: 0.7)
  format?: string;               // 'webp', 'jpeg', 'png' (default: 'webp')
  maxWidth?: number;             // Maximum width in pixels
  maxHeight?: number;            // Maximum height in pixels
  enableLogging?: boolean;       // Show compression stats (default: false)
  logLevel?: string;             // 'basic' or 'detailed' (default: 'basic')
  preferredMethod?: string;      // 'auto', 'canvas', 'worker', 'wasm', 'lightweight'
}

Example:

javascript

const result = await compressImage(file, {
  quality: 0.8,
  format: 'webp',
  maxWidth: 1920,
  maxHeight: 1080,
  enableLogging: true,
  logLevel: 'detailed',
  preferredMethod: 'auto'
});

compressMultiple(images, options)

Compress multiple images with the same settings.

Parameters:

  • images - Array of File, Blob, HTMLImageElement, or data URL strings
  • options - Same as compressImage options

Returns: Promise resolving to array of compression results

Example:

javascript

const fileInput = document.querySelector('input[type="file"]');
const files = Array.from(fileInput.files);

const results = await compressMultiple(files, {
  quality: QUALITY_PRESETS.MEDIUM,
  format: 'webp',
  maxWidth: 1920
});

results.forEach((result, index) => {
  if (result.error) {
    console.error(`Failed to compress image ${index}:`, result.error);
  } else {
    console.log(`Image ${index} compressed by ${result.stats.compressionRatio}%`);
    console.log(`Method used: ${result.method}`);
  }
});

createCompressor(defaultOptions)

Create a reusable compressor with preset options.

Parameters:

  • defaultOptions - Default compression options

Returns: Compressor object with compress and compressMultiple methods

Example:

javascript

// Create specialized compressors for different use cases
const thumbnailCompressor = createCompressor({
  quality: QUALITY_PRESETS.LOW,
  format: 'webp',
  maxWidth: 300,
  maxHeight: 300,
  enableLogging: false
});

const highQualityCompressor = createCompressor({
  quality: QUALITY_PRESETS.HIGH,
  format: 'webp',
  maxWidth: 1920,
  enableLogging: true,
  logLevel: 'detailed'
});

// Use the compressors
const thumbnail = await thumbnailCompressor.compress(file);
const highQuality = await highQualityCompressor.compress(file, {
  quality: 0.95  // Override default quality
});

Quality Presets

Pre-defined quality settings for common use cases:

javascript

import { QUALITY_PRESETS } from 'image-compressor-by-xettri-aleen';

QUALITY_PRESETS.LOW      // 0.4 - Heavy compression, good for thumbnails
QUALITY_PRESETS.MEDIUM   // 0.7 - Balanced size and quality
QUALITY_PRESETS.HIGH     // 0.9 - High quality, larger files
QUALITY_PRESETS.LOSSLESS // 1.0 - No quality loss

// Usage examples
const thumbnail = await compressImage(file, {
  quality: QUALITY_PRESETS.LOW,
  maxWidth: 200
});

const webOptimized = await compressImage(file, {
  quality: QUALITY_PRESETS.MEDIUM,
  format: 'webp'
});

const printQuality = await compressImage(file, {
  quality: QUALITY_PRESETS.HIGH,
  format: 'jpeg'
});

Format Options

javascript

import { FORMAT_OPTIONS } from 'image-compressor-by-xettri-aleen';

FORMAT_OPTIONS.WEBP  // 'webp' - Best compression, modern browsers
FORMAT_OPTIONS.JPEG  // 'jpeg' - Good compatibility, lossy compression
FORMAT_OPTIONS.PNG   // 'png' - Supports transparency, lossless

// Usage examples
const webpImage = await compressImage(file, {
  format: FORMAT_OPTIONS.WEBP,
  quality: 0.8
});

const jpegImage = await compressImage(file, {
  format: FORMAT_OPTIONS.JPEG,
  quality: 0.85
});

const pngImage = await compressImage(file, {
  format: FORMAT_OPTIONS.PNG,
  quality: 1.0  // PNG quality affects compression level, not visual quality
});

🎯 Compression Methods

image-compressor-by-xettri-aleen automatically selects the best available method based on browser capabilities and CSP restrictions:

1. Canvas Method (Default)

  • Best Performance - Direct browser APIs, fastest processing
  • Requirements - Canvas access, no strict CSP blocking canvas operations
  • Features - Full format support, high-quality output, real-time processing

javascript

// Force canvas method
const result = await compressImage(file, {
  preferredMethod: 'canvas',
  quality: 0.8,
  format: 'webp'
});

2. Web Worker Method

  • Good Performance - Offscreen processing, non-blocking UI
  • Requirements - Web Workers enabled, OffscreenCanvas support
  • Features - Background processing, CSP-friendly, maintains UI responsiveness

javascript

// Force worker method
const result = await compressImage(file, {
  preferredMethod: 'worker',
  quality: 0.8,
  format: 'webp'
});

3. WebAssembly Method

  • High Quality - Advanced compression algorithms via browser-image-compression
  • Requirements - WASM support, external library dependency
  • Features - Best compression ratios, multiple format optimizations

javascript

// Force WASM method (requires browser-image-compression dependency)
const result = await compressImage(file, {
  preferredMethod: 'wasm',
  quality: 0.8,
  format: 'webp'
});

4. Lightweight Method

  • Universal Compatibility - Pure JavaScript, works everywhere
  • Requirements - Basic browser APIs only
  • Features - Maximum compatibility, fallback for restricted environments

javascript

// Force lightweight method
const result = await compressImage(file, {
  preferredMethod: 'lightweight',
  quality: 0.8,
  format: 'webp'
});

🔧 Advanced Configuration

Automatic Method Selection

The library automatically chooses the best method:

javascript

// Automatic selection (recommended)
const result = await compressImage(file, {
  quality: 0.8,
  format: 'webp',
  enableLogging: true  // See which method was chosen
});

console.log(`Used method: ${result.method}`);

Size Constraints and Aspect Ratio

javascript

// Resize while maintaining aspect ratio
const result = await compressImage(file, {
  maxWidth: 800,      // Maximum width
  maxHeight: 600,     // Maximum height
  quality: 0.9,
  format: 'jpeg'
});

// Create different sizes for responsive images
const sizes = [
  { name: 'thumbnail', maxWidth: 150, maxHeight: 150 },
  { name: 'medium', maxWidth: 800, maxHeight: 600 },
  { name: 'large', maxWidth: 1920, maxHeight: 1080 }
];

const responsiveImages = await Promise.all(
  sizes.map(size =>
    compressImage(file, {
      ...size,
      quality: QUALITY_PRESETS.MEDIUM,
      format: 'webp'
    })
  )
);

Batch Processing with Progress Tracking

javascript

const files = Array.from(fileInput.files);
const results = [];
let processed = 0;

// Process with progress updates
for (const file of files) {
  try {
    console.log(`Processing ${processed + 1}/${files.length}: ${file.name}`);

    const result = await compressImage(file, {
      quality: QUALITY_PRESETS.MEDIUM,
      format: 'webp',
      maxWidth: 1920,
      enableLogging: false  // Disable for batch processing
    });

    results.push(result);
    processed++;

    // Update progress UI
    const progressPercent = (processed / files.length) * 100;
    updateProgressBar(progressPercent);

  } catch (error) {
    console.error(`Failed to process ${file.name}:`, error);
    results.push({ error: error.message, file: file.name });
  }
}

console.log(`Processed ${processed}/${files.length} images successfully`);

Format-Specific Optimizations

javascript

// WebP with maximum compression
const webpResult = await compressImage(file, {
  format: 'webp',
  quality: 0.8,
  maxWidth: 1920
});

// JPEG with progressive encoding simulation
const jpegResult = await compressImage(file, {
  format: 'jpeg',
  quality: 0.85,
  maxWidth: 1920
});

// PNG with transparency preservation
const pngResult = await compressImage(file, {
  format: 'png',
  quality: 1.0,  // PNG: 1.0 = best compression, 0.0 = fastest
  maxWidth: 1920
});

// Compare results
console.log('WebP size:', webpResult.stats.compressedSize);
console.log('JPEG size:', jpegResult.stats.compressedSize);
console.log('PNG size:', pngResult.stats.compressedSize);

Error Handling and Fallbacks

javascript

async function robustCompression(file, options = {}) {
  const methods = ['auto', 'canvas', 'worker', 'lightweight'];

  for (const method of methods) {
    try {
      const result = await compressImage(file, {
        ...options,
        preferredMethod: method,
        enableLogging: true
      });

      console.log(`Success with ${method} method`);
      return result;

    } catch (error) {
      console.warn(`${method} method failed:`, error.message);

      if (method === 'lightweight') {
        throw new Error('All compression methods failed');
      }
    }
  }
}

// Usage
try {
  const result = await robustCompression(file, {
    quality: 0.8,
    format: 'webp'
  });
  console.log('Compression successful:', result.stats);
} catch (error) {
  console.error('Compression completely failed:', error);
}

Memory Management for Large Batches

javascript

async function processBatchWithMemoryManagement(files, options = {}) {
  const results = [];
  const batchSize = 5; // Process 5 images at a time

  for (let i = 0; i < files.length; i += batchSize) {
    const batch = files.slice(i, i + batchSize);

    console.log(`Processing batch ${Math.floor(i/batchSize) + 1}/${Math.ceil(files.length/batchSize)}`);

    const batchResults = await Promise.all(
      batch.map(async (file) => {
        try {
          const result = await compressImage(file, options);
          return result;
        } catch (error) {
          return { error: error.message, file: file.name };
        }
      })
    );

    results.push(...batchResults);

    // Force garbage collection hint
    if (window.gc) window.gc();

    // Small delay to prevent UI blocking
    await new Promise(resolve => setTimeout(resolve, 100));
  }

  return results;
}

📊 Result Object

The compression result contains comprehensive information:

javascript

{
  blob: Blob,           // Compressed image as Blob object
  file: File,           // Compressed image as File object with proper name
  stats: {
    originalSize: 1048576,      // Original size in bytes
    compressedSize: 524288,     // Compressed size in bytes
    savedBytes: 524288,         // Bytes saved (originalSize - compressedSize)
    compressionRatio: 50,       // Percentage reduction
    format: 'WEBP'             // Output format (uppercase)
  },
  method: 'canvas'      // Compression method actually used
}

// Usage examples
const result = await compressImage(file);

// Access the compressed image
const compressedBlob = result.blob;
const compressedFile = result.file;

// Get compression statistics
console.log(`Original: ${result.stats.originalSize} bytes`);
console.log(`Compressed: ${result.stats.compressedSize} bytes`);
console.log(`Saved: ${result.stats.savedBytes} bytes (${result.stats.compressionRatio}%)`);
console.log(`Method: ${result.method}`);
console.log(`Format: ${result.stats.format}`);

// Create download link
const url = URL.createObjectURL(result.blob);
const downloadLink = document.createElement('a');
downloadLink.href = url;
downloadLink.download = result.file.name;
downloadLink.textContent = 'Download Compressed Image';
document.body.appendChild(downloadLink);

// Display compressed image
const img = document.createElement('img');
img.src = url;
img.onload = () => URL.revokeObjectURL(url); // Clean up
document.body.appendChild(img);

🌍 Browser Compatibility

Desktop Browsers

| Browser | Canvas | Worker | WASM | Lightweight | WebP | Notes | | --- | --- | --- | --- | --- | --- | --- | | Chrome 60+ | ✅ | ✅ | ✅ | ✅ | ✅ | Full support | | Firefox 55+ | ✅ | ✅ | ✅ | ✅ | ✅ | Full support | | Safari 14+ | ✅ | ✅ | ✅ | ✅ | ✅ | WebP since 14.0 | | Edge 79+ | ✅ | ✅ | ✅ | ✅ | ✅ | Chromium-based | | IE 11 | ❌ | ❌ | ❌ | ⚠️ | ❌ | Limited support |

Mobile Browsers

| Browser | Canvas | Worker | WASM | Lightweight | WebP | Notes | | --- | --- | --- | --- | --- | --- | --- | | Chrome Mobile | ✅ | ✅ | ✅ | ✅ | ✅ | Full support | | Safari iOS | ✅ | ✅ | ✅ | ✅ | ✅ | iOS 14+ for WebP | | Firefox Mobile | ✅ | ✅ | ✅ | ✅ | ✅ | Full support | | Samsung Internet | ✅ | ✅ | ✅ | ✅ | ✅ | Full support |

CSP Compatibility

| CSP Directive | Canvas | Worker | WASM | Lightweight | Workaround | | --- | --- | --- | --- | --- | --- | | default-src 'self' | ⚠️ | ✅ | ✅ | ✅ | Auto-fallback to Worker | | img-src 'self' | ⚠️ | ✅ | ✅ | ✅ | Blocks blob URLs | | worker-src 'self' | ✅ | ⚠️ | ✅ | ✅ | Auto-fallback to WASM | | script-src 'unsafe-eval' | ✅ | ✅ | ⚠️ | ✅ | Auto-fallback to Lightweight |

Feature Detection

javascript

import { detectCapabilities } from 'image-compressor-by-xettri-aleen/utils';

// Check what your environment supports
const capabilities = await detectCapabilities();

console.log('Canvas support:', capabilities.canvas);
console.log('Web Workers:', capabilities.webWorkers);
console.log('WebAssembly:', capabilities.webAssembly);
console.log('WebP support:', capabilities.formats.webp);

// Use capabilities to make decisions
if (!capabilities.formats.webp) {
  // Fall back to JPEG for older browsers
  const result = await compressImage(file, {
    format: 'jpeg',
    quality: 0.85
  });
}

🚀 Performance

Benchmark Results

Tested on 5MB JPEG image (4000x3000px), Intel i7, Chrome 120

| Method | Processing Time | Memory Usage | Quality | Compression Ratio | | --- | --- | --- | --- | --- | | Canvas | 150ms | Low | High | 75% | | Worker | 180ms | Low | High | 75% | | WASM | 240ms | Medium | Excellent | 80% | | Lightweight | 200ms | Low | Good | 70% |

Performance by Image Size

| Image Size | Canvas | Worker | WASM | Lightweight | | --- | --- | --- | --- | --- | | < 1MB | 50ms | 70ms | 100ms | 80ms | | 1-5MB | 150ms | 180ms | 240ms | 200ms | | 5-10MB | 400ms | 450ms | 600ms | 500ms | | > 10MB | 800ms | 900ms | 1200ms | 1000ms |

Optimization Tips

  1. Choose the Right Format

    javascript

    // WebP for best compression
    const webp = await compressImage(file, { format: 'webp', quality: 0.8 });
    
    // JPEG for compatibility
    const jpeg = await compressImage(file, { format: 'jpeg', quality: 0.85 });
    
    // PNG only when transparency is needed
    const png = await compressImage(file, { format: 'png', quality: 1.0 });
  2. Resize Before Compression

    javascript

    // Resize large images first
    const optimized = await compressImage(file, {
      maxWidth: 1920,    // Resize to reasonable dimensions
      maxHeight: 1080,
      quality: 0.8,
      format: 'webp'
    });
  3. Use Appropriate Quality Settings

    javascript

    // Different quality for different use cases
    const thumbnail = await compressImage(file, {
      quality: QUALITY_PRESETS.LOW,    // 0.4 for thumbnails
      maxWidth: 300
    });
    
    const webDisplay = await compressImage(file, {
      quality: QUALITY_PRESETS.MEDIUM, // 0.7 for web display
      maxWidth: 1200
    });
    
    const print = await compressImage(file, {
      quality: QUALITY_PRESETS.HIGH,   // 0.9 for print quality
      maxWidth: 3000
    });
  4. Batch Processing Optimization

    javascript

    // Process in smaller batches to avoid memory issues
    const batchSize = 5;
    for (let i = 0; i < files.length; i += batchSize) {
      const batch = files.slice(i, i + batchSize);
      await Promise.all(batch.map(file => compressImage(file, options)));
    
      // Give the browser time to clean up
      await new Promise(resolve => setTimeout(resolve, 100));
    }
  5. Disable Logging in Production

    javascript

    const result = await compressImage(file, {
      quality: 0.8,
      enableLogging: false  // Disable logging for better performance
    });

🔒 Security

CSP Safety

  • No External Requests - All processing happens locally in the browser
  • Automatic Fallbacks - Gracefully handles CSP restrictions
  • Memory Safety - Automatic cleanup of temporary objects and blob URLs

Input Validation

javascript

// The library automatically validates inputs
try {
  const result = await compressImage(invalidInput, options);
} catch (error) {
  console.error('Invalid input:', error.message);
  // Handle validation errors gracefully
}

Secure Usage Patterns

javascript

// Always clean up object URLs
const result = await compressImage(file);
const url = URL.createObjectURL(result.blob);

// Use the URL
displayImage(url);

// Clean up when done
URL.revokeObjectURL(url);

// Or use automatic cleanup
const img = new Image();
img.onload = () => URL.revokeObjectURL(img.src);
img.src = URL.createObjectURL(result.blob);

🛠️ Development

Building from Source

bash

# Clone the repository
git clone https://github.com/xettrialeen/open_source_packages/tree/main/image-compressor-by-xettri-aleen.git
cd image-compressor-by-xettri-aleen

# Install dependencies
npm install

# Build all formats
npm run build

# Build for production
npm run build:prod

# Development build with watch
npm run dev

Project Structure

image-compressor-by-xettri-aleen/
├── src/
│   ├── index.js                    # Main entry point
│   ├── standalone-index.js         # Standalone build entry
│   ├── core/
│   │   ├── compressor.js          # Main compression logic
│   │   ├── canvas-handler.js      # Canvas compression method
│   │   ├── worker-handler.js      # Web Worker method
│   │   ├── wasm-handler.js        # WebAssembly method
│   │   └── light-weight-handler.js # Lightweight fallback
│   ├── utils/
│   │   ├── detection.js           # Browser capability detection
│   │   ├── helpers.js             # Utility functions
│   │   └── logger.js              # Logging utilities
│   └── workers/
│       └── compression-worker.js   # Standalone worker script
├── dist/
│   ├── index.js                   # UMD build
│   ├── index.esm.js              # ES module build
│   └── index.standalone.js        # Standalone build
├── types/
│   └── index.d.ts                # TypeScript definitions
├── test/
│   ├── basic-test.html           # Basic functionality tests
│   ├── esm-test.html             # ES module tests
│   ├── standalone-test.html      # Standalone build tests
│   └── performance-test.html     # Performance benchmarks
├── package.json
├── rollup.config.js              # Build configuration
├── README.md
└── LICENSE