Canvas API v1.0.1 Zero Dependencies MIT License

ImageProcessor.js

A lightweight library for client-side image processing using the HTML5 Canvas API — filters, crop, resize, rotate, and watermarks entirely in the browser. No server, no dependencies.

CSS Filters Aren't Enough

CSS filters change how an image looks — they never touch the underlying pixel data. That means you can't export the result, enforce a watermark, or generate a resized thumbnail. Whenever you need the processed image to actually exist, you reach for the Canvas API — and that's where the boilerplate piles up fast:

const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = () => {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  canvas.width = 800; canvas.height = 600;
  ctx.filter = 'grayscale(100%) brightness(1.2)';
  ctx.drawImage(img, 0, 0, 800, 600);
  // ... watermark logic ...
  // ... inject into DOM ...
};
img.src = url;

Multiply that by every image on the page, add watermarks, rotation, batch processing, and error callbacks — and you've reinvented ImageProcessor.js. Instead:

new ImageProcessor('https://example.com/photo.jpg', {
  width: 800,
  grayscale: 'grayscale(100%)',
  brightness: 'brightness(1.2)',
  watermark: '© 2026 Peter Benoit',
  targetElement: document.getElementById('output'),
});

CSS Filters vs. ImageProcessor

The key distinction: CSS is visual-only. ImageProcessor operates on actual pixel data.

Capability CSS Filters ImageProcessor
Apply visual effects (blur, brightness, etc.)
Export / save the modified image
Crop or resize actual pixel data
Bake in watermarks (not bypassable with DevTools)
Batch-process multiple images
Generate thumbnails before upload
Consistent cross-browser output Partial

CSS Filter Syntax

Filters use the native CSS filter function syntax you already know. They're applied via the Canvas 2D filter property and baked into the output pixels.

new ImageProcessor(url, {
  grayscale:  'grayscale(100%)',
  brightness: 'brightness(1.4)',
  contrast:   'contrast(1.2)',
  blur:       'blur(4px)',
  sepia:      'sepia(80%)',
  targetElement: el,
});

Watermarks

Text or image watermarks, composited at the pixel level. Position, angle, repeat pattern, and style are all configurable.

new ImageProcessor(url, {
  watermark: '© 2026 My Site',
  watermarkPosition: 'bottom-right',
  watermarkAngle: -30,
  watermarkRepeat: 'repeat',
  watermarkStyle: {
    fontSize: '18px',
    color: 'rgba(255,255,255,0.4)',
  },
  targetElement: el,
});

Crop, Resize & Rotate

Transforms are applied via the canvas coordinate system during drawImage() — no intermediate copies, no quality loss from double-processing.

new ImageProcessor(url, {
  width: 400, height: 300,
  cropX: 50, cropY: 50,
  cropWidth: 500, cropHeight: 400,
  rotate: 90,
  targetElement: el,
});

Batch & Data Attribute API

Process dozens of images with one call, or use data-img attributes for zero-JS declarative usage — great for CMS-generated markup.

<!-- HTML-only, no JS needed per element -->
<div
  data-img="photo.jpg"
  data-grayscale="true"
  data-watermark="© My Site"
  data-width="600"
></div>

// One line to process all
new ImageProcessor();

How It Works

No magic — just a thin wrapper around APIs your browser already ships with.

  1. 1. A hidden <img> loads the source URL with the configured crossOrigin, loading, decoding, and referrerpolicy attributes.
  2. 2. On load, the image is drawn onto an off-screen <canvas> with the composed CSS filter string applied via the Canvas 2D filter property.
  3. 3. Crop, resize, and rotation transforms are applied during drawImage() using the canvas coordinate system.
  4. 4. If a watermark is specified, it is drawn on top — either as composited text or as a second image via drawImage().
  5. 5. The canvas is exported via canvas.toDataURL(format, quality) and injected into the target DOM element as an <img> tag.
  6. 6. Lifecycle callbacks (onProcessingStart, onProcessed, onError) fire at each step.

The original source image is never mutated. The output is a new, independent image that can be saved, uploaded, or displayed separately.

Options Reference

All options passed as the second argument to new ImageProcessor(url, options).

Dimensions & Transforms

Option Default Description
width null Output width in pixels. Uses source width if omitted.
height null Output height in pixels. Uses source height if omitted.
cropX 0 X offset of the crop region on the source image.
cropY 0 Y offset of the crop region on the source image.
cropWidth null Width of the crop region. Uses full image width if omitted.
cropHeight null Height of the crop region. Uses full image height if omitted.
rotate 0 Rotation angle in degrees.

Filters (CSS filter function syntax)

Option Default Example value
grayscale '' 'grayscale(100%)'
sepia '' 'sepia(80%)'
invert '' 'invert(100%)'
brightness 'brightness(1)' 'brightness(1.5)'
contrast 'contrast(1)' 'contrast(1.2)'
blur '' 'blur(4px)'
saturation 'saturate(1)' 'saturate(2)'
hueRotate '' 'hue-rotate(90deg)'
opacity 'opacity(1)' 'opacity(0.6)'

Watermark

Option Default Description
watermark null Text string or image URL to overlay as a watermark.
watermarkPosition 'bottom-right' 'top-left', 'top-right', 'center', 'bottom-left', 'bottom-right'
watermarkRepeat 'no-repeat' 'no-repeat', 'repeat', or 'cover'
watermarkAngle 0 Rotation angle for text watermarks, in degrees.
watermarkStyle See docs Style object: fontSize, fontFamily, color, opacity.

Output, Loading & Callbacks

Option Default Description
outputFormat 'image/png' Canvas export format. Use 'image/jpeg' for photos.
quality 0.92 Quality for lossy formats (0.0–1.0).
loading 'auto' 'lazy', 'eager', or 'auto'.
crossorigin 'anonymous' 'anonymous' or 'use-credentials'.
altText 'Processed image' alt attribute for the output <img>. Always set a meaningful value.
targetElement null DOM node where the processed image is injected. Required for programmatic use.
onProcessingStart Fired when processing begins. Receives { imageUrl, targetElement }.
onProcessed Fired when the image is in the DOM. Receives { imageUrl, targetElement }.
onError Fired on failure. Receives { imageUrl, error, targetElement }.

Usage

A single script tag — no bundler, no build step.

Include

<script src="path/to/ImageProcessor.js"></script>

Single image

new ImageProcessor('photo.jpg', {
  width: 400, height: 300,
  brightness: 'brightness(1.4)',
  contrast: 'contrast(1.2)',
  watermark: '© 2026',
  altText: 'Enhanced landscape photo',
  targetElement: document.getElementById('output'),
  onProcessed: ({ imageUrl }) => console.log('Done:', imageUrl),
});

Batch processing

new ImageProcessor([
  { url: 'img1.jpg', options: { grayscale: 'grayscale(100%)', targetElement: el1 } },
  { url: 'img2.jpg', options: { sepia: 'sepia(80%)',         targetElement: el2 } },
  { url: 'img3.jpg', options: { blur: 'blur(3px)',           targetElement: el3 } },
]);

Sensitive image blurring

<!-- Blurred by default, click to reveal -->
<div class="sensitive-image" data-img="photo.jpg" data-alt="..."></div>
<script>new ImageProcessor();</script>

The blur is applied at the canvas level — not a CSS class — so it can't be bypassed through DevTools.