Works with any backend npm install Zero dependencies

Full Image Pipeline

Combine resize, compress, auto-orient, watermark, and hash in one configuration.

(function() {
 var logEl = document.getElementById('log');
 function log(m) { var d = document.createElement('div'); d.className='pkg-log-entry'; d.textContent=m; logEl.appendChild(d); logEl.scrollTop=logEl.scrollHeight; }
 new MultipleUpload('#demo', {
 uploadUrl: '/api/upload',
 multiple: true,
 accept: 'image/*',
 showThumbnails: true,
 autoOrient: true,
 imageResize: { maxWidth: 1280, maxHeight: 1280, quality: 0.88 },
 imageCompress: { quality: 0.78, outputType: 'image/jpeg' },
 watermark: { text: 'SAMPLE', fontSize: 30, position: 'bottom-right', color: 'rgba(0,0,0,0.25)', padding: 16 },
 computeHash: true,
 hashAlgorithm: 'sha256',
 onImageProcessed: function(p, o) { log(o.name + ': ' + Math.round(o.size/1024) + 'KB -> ' + Math.round(p.size/1024) + 'KB'); },
 onHashComputed: function(t, h) { log(t.fileName + ' hash: ' + h.slice(0,20) + '...'); }
 });
})();

The stages, in the order they run

The built-in image steps compose: EXIF auto-orient, then resize to bounds, then compress, then watermark, then any transformPipeline functions of your own. Each hands its output to the next, so what reaches the server is the result of all of them.

Order matters more than it looks

Auto-orient has to come first, or a resize computes bounds against a sideways image. Watermarking after resizing keeps the mark proportional to the final picture rather than scaled down with it. This is why the order is fixed rather than configurable — the alternatives mostly produce wrong output.