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.
A hidden
<img>loads the source URL with the configuredcrossOrigin,loading,decoding, andreferrerpolicyattributes. -
2.
On load, the image is drawn onto an off-screen
<canvas>with the composed CSS filter string applied via the Canvas 2Dfilterproperty. -
3.
Crop, resize, and rotation transforms are applied during
drawImage()using the canvas coordinate system. -
4.
If a watermark is specified, it is drawn on top. Either as composited text or as a second
image via
drawImage(). -
5.
The canvas is exported via
canvas.toDataURL(format, quality)and injected into the target DOM element as an<img>tag. -
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.