Image Compressor

Image Compressor — Reduce Image File Size Online Free

Compress images without losing quality

Shrink JPG, PNG, and WebP files by up to 90% right in your browser. No uploads, no waiting, no limits.

100% private — runs on your device No file size limits Free, no sign-up

Drop images here or click to browse

Compress multiple files at once

Choose Files
Supports JPG, PNG and WebP · Files are processed locally and never uploaded
80%
Balanced — good quality, strong compression
0%
0
Files
0 KB
Original size
0 KB
Compressed size
0 KB
Total saved
'; } else if(item.status === 'error'){ sizeRowHTML = '
' + (item.error || 'Could not compress this image.') + '
'; } else { sizeRowHTML = '
' + formatBytes(item.originalSize) + '
'; } var downloadDisabled = item.status !== 'done'; card.innerHTML = '
' + '' + '' + badgeHTML + '
' + '
' + item.file.name + '
' + sizeRowHTML + ''; card.querySelector('[data-action="remove"]').addEventListener('click', function(){ removeItem(item.id); }); var dlBtn = card.querySelector('[data-action="download"]'); if(dlBtn && !downloadDisabled){ dlBtn.addEventListener('click', function(){ downloadSingle(item); }); } return card; } function updateCardInDom(item){ var existing = resultsGrid.querySelector('[data-id="' + item.id + '"]'); var fresh = buildCard(item); if(existing){ resultsGrid.replaceChild(fresh, existing); } else { resultsGrid.appendChild(fresh); } } function removeItem(id){ var idx = items.findIndex(function(i){ return i.id === id; }); if(idx === -1) return; var item = items[idx]; if(item.originalURL) URL.revokeObjectURL(item.originalURL); if(item.compressedURL) URL.revokeObjectURL(item.compressedURL); items.splice(idx, 1); var node = resultsGrid.querySelector('[data-id="' + id + '"]'); if(node) node.remove(); if(items.length === 0){ settingsPanel.classList.remove('visible'); statsBar.classList.remove('visible'); } updateStats(); } /* ---------- Stats ---------- */ function updateStats(){ var totalFiles = items.length; var doneItems = items.filter(function(i){ return i.status === 'done'; }); var originalTotal = items.reduce(function(sum,i){ return sum + i.originalSize; }, 0); // Pending/error items count toward "compressed" total as their original size (no savings yet) var displayedCompressed = items.reduce(function(sum,i){ return sum + (i.status === 'done' ? i.compressedSize : i.originalSize); }, 0); var saved = Math.max(0, originalTotal - displayedCompressed); var pct = originalTotal > 0 ? Math.round((saved / originalTotal) * 100) : 0; statFiles.textContent = totalFiles; statOriginal.textContent = formatBytes(originalTotal); statCompressed.textContent = formatBytes(displayedCompressed); statSaved.textContent = formatBytes(saved); var offset = RING_CIRCUMFERENCE - (RING_CIRCUMFERENCE * pct / 100); ringFg.style.strokeDashoffset = offset; ringLabel.textContent = pct + '%'; if(totalFiles > 0){ statsBar.classList.add('visible'); } downloadAllBtn.disabled = doneItems.length === 0; } /* ---------- Compression core ---------- */ function loadImage(file){ return new Promise(function(resolve, reject){ var url = URL.createObjectURL(file); var img = new Image(); img.onload = function(){ resolve({ img: img, url: url }); }; img.onerror = function(){ URL.revokeObjectURL(url); reject(new Error('Could not read this image.')); }; img.src = url; }); } function compressOne(item){ var quality = parseInt(qualityRange.value, 10) / 100; var formatChoice = formatSelect.value; var maxWidth = resizeToggle.checked ? parseInt(maxWidthInput.value, 10) : 0; return loadImage(item.file).then(function(res){ var img = res.img; var width = img.naturalWidth; var height = img.naturalHeight; if(maxWidth && maxWidth > 0 && width > maxWidth){ height = Math.round(height * (maxWidth / width)); width = maxWidth; } var canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; var ctx = canvas.getContext('2d'); ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = 'high'; var mime = formatChoice === 'keep' ? (item.file.type || 'image/jpeg') : formatChoice; if(mime === 'image/jpeg'){ ctx.fillStyle = '#FFFFFF'; ctx.fillRect(0, 0, width, height); } ctx.drawImage(img, 0, 0, width, height); URL.revokeObjectURL(res.url); var qParam = (mime === 'image/png') ? undefined : quality; return new Promise(function(resolve, reject){ canvas.toBlob(function(blob){ if(!blob){ reject(new Error('Compression failed for this image.')); return; } resolve({ blob: blob, mime: mime, width: width, height: height }); }, mime, qParam); }); }); } function runCompression(){ if(items.length === 0) return; compressBtn.disabled = true; var queue = items.slice(); var i = 0; function next(){ if(i >= queue.length){ compressBtn.disabled = false; announce('Compression complete.'); return; } var item = queue[i]; item.status = 'processing'; item.error = null; updateCardInDom(item); compressOne(item).then(function(result){ if(item.compressedURL) URL.revokeObjectURL(item.compressedURL); item.compressedBlob = result.blob; item.compressedSize = result.blob.size; item.compressedURL = URL.createObjectURL(result.blob); item.outWidth = result.width; item.outHeight = result.height; item.status = 'done'; updateCardInDom(item); updateStats(); i++; next(); }).catch(function(err){ item.status = 'error'; item.error = err && err.message ? err.message : 'Compression failed.'; updateCardInDom(item); updateStats(); i++; next(); }); } next(); } compressBtn.addEventListener('click', runCompression); /* ---------- Downloads ---------- */ function downloadFileName(item){ var mime = item.compressedBlob ? item.compressedBlob.type : item.file.type; var ext = extFromMime(mime) || ('.' + (item.file.name.split('.').pop() || 'img')); return baseName(item.file.name) + '-compressed' + ext; } function downloadSingle(item){ if(!item.compressedBlob) return; var a = document.createElement('a'); a.href = item.compressedURL; a.download = downloadFileName(item); document.body.appendChild(a); a.click(); a.remove(); } downloadAllBtn.addEventListener('click', function(){ var doneItems = items.filter(function(i){ return i.status === 'done'; }); if(doneItems.length === 0) return; if(typeof JSZip === 'undefined'){ announce('Could not load ZIP library — downloading files individually instead.'); doneItems.forEach(function(item){ downloadSingle(item); }); return; } downloadAllBtn.disabled = true; var originalLabel = downloadAllBtn.innerHTML; downloadAllBtn.innerHTML = ' Zipping...'; var zip = new JSZip(); var usedNames = {}; doneItems.forEach(function(item){ var name = downloadFileName(item); if(usedNames[name]){ usedNames[name]++; var dot = name.lastIndexOf('.'); name = name.slice(0, dot) + '-' + usedNames[name] + name.slice(dot); } else { usedNames[name] = 1; } zip.file(name, item.compressedBlob); }); zip.generateAsync({ type: 'blob' }).then(function(content){ var url = URL.createObjectURL(content); var a = document.createElement('a'); a.href = url; a.download = 'compressed-images.zip'; document.body.appendChild(a); a.click(); a.remove(); setTimeout(function(){ URL.revokeObjectURL(url); }, 4000); downloadAllBtn.disabled = false; downloadAllBtn.innerHTML = originalLabel; announce('ZIP file downloaded.'); }).catch(function(){ downloadAllBtn.disabled = false; downloadAllBtn.innerHTML = originalLabel; announce('Could not create ZIP file. Please try downloading images individually.'); }); }); /* ---------- Clear all ---------- */ clearAllBtn.addEventListener('click', function(){ items.forEach(function(item){ if(item.originalURL) URL.revokeObjectURL(item.originalURL); if(item.compressedURL) URL.revokeObjectURL(item.compressedURL); }); items = []; resultsGrid.innerHTML = ''; settingsPanel.classList.remove('visible'); statsBar.classList.remove('visible'); downloadAllBtn.disabled = true; ringFg.style.strokeDashoffset = RING_CIRCUMFERENCE; ringLabel.textContent = '0%'; announce('All images cleared.'); }); })();

This website uses cookies.