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 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'),
});
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 | ✓ |
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,
});
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,
});
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,
});
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();
No magic — just a thin wrapper around APIs your browser already ships with.
<img> loads the source URL with the configured
crossOrigin, loading, decoding, and
referrerpolicy attributes.
<canvas> with the
composed CSS filter string applied via the Canvas 2D filter property.
drawImage() using
the canvas coordinate system.
drawImage().
canvas.toDataURL(format, quality) and injected
into the target DOM element as an <img> tag.
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.
All options passed as the second argument to
new ImageProcessor(url, options).
| 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. |
| 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)' |
| 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. |
| 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 }. |
A single script tag — no bundler, no build step.
<script src="path/to/ImageProcessor.js"></script>
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),
});
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 } },
]);
<!-- 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.