Density — image compressor & converter

client-side photo lab

DENSITY

Drop in an image, dial down its density, and carry it out lighter — JPEG, PNG, WebP or AVIF.

Drop images here, or click to browse

JPEG · PNG · WEBP · GIF · BMP · SVG · AVIF — not saved in our server.

0 images

AVIF/WebP output availability depends on your browser’s encoder support.

' + '
' + '
' + '
' + '

' + escapeHtml(card.name) + '

' + '
' + '' + card.originalExt + '' + '' + card.naturalWidth + '×' + card.naturalHeight + '' + '' + formatBytes(card.originalSize) + '' + '
' + '
' + '
' + '
' + '
Compressionf/5.6 · 75% · Balanced
' + '' + '
' + stopsHtml + '
' + '
' + '
' + '
Convert to
' + '
' + chipButtons + '
' + '
' + '' + '' + '
' + '
' + '
' + '
' + '
Original density' + formatBytes(card.originalSize) + '
' + '' + '
New density
' + '' + '
' + '' + '
' + '
' + '
'; el.querySelector('[data-action="remove"]').addEventListener('click', function(){ removeCard(card.id); }); var qSlider = el.querySelector('[data-role="quality"]'); qSlider.addEventListener('input', function(){ card.settings.qIndex = parseInt(qSlider.value, 10); updateQualityLabel(card); setChipsLoading(card); clearTimeout(card.debounceTimer); card.debounceTimer = setTimeout(function(){ recomputeCard(card); }, 300); }); el.querySelectorAll('[data-role="chips"] .chip').forEach(function(btn){ btn.addEventListener('click', function(){ if (btn.disabled) return; var fmt = btn.dataset.fmt; card.settings.format = fmt; updateChipActive(card); updateResultPanel(card); }); }); var resizeToggle = el.querySelector('[data-role="resize-toggle"]'); var resizeFields = el.querySelector('[data-role="resize-fields"]'); resizeToggle.addEventListener('change', function(){ card.settings.resize = resizeToggle.checked; resizeFields.hidden = !resizeToggle.checked; recomputeCard(card); }); var resizeInput = el.querySelector('[data-role="resize-input"]'); resizeInput.addEventListener('input', function(){ var v = parseInt(resizeInput.value, 10); if (isNaN(v)) return; v = Math.max(50, Math.min(20000, v)); card.settings.maxDim = v; clearTimeout(card.debounceTimer); card.debounceTimer = setTimeout(function(){ recomputeCard(card); }, 400); }); el.querySelector('[data-role="download"]').addEventListener('click', function(){ if (!card.downloadURL) return; var fmt = card.settings.format; triggerDownload(card.downloadURL, card.base + '.' + EXT[fmt]); }); updateQualityLabel(card); updateChipActive(card); } function updateQualityLabel(card){ var stop = QUALITY_STOPS[card.settings.qIndex]; var labelEl = card.el.querySelector('[data-role="qlabel"]'); if (labelEl) labelEl.textContent = stop.label + ' · ' + stop.q + '% · ' + stop.desc; card.el.querySelectorAll('[data-role="stops"] span').forEach(function(s){ s.classList.toggle('active', parseInt(s.dataset.i, 10) === card.settings.qIndex); }); } function updateChipActive(card){ card.el.querySelectorAll('[data-role="chips"] .chip').forEach(function(btn){ btn.classList.toggle('active', btn.dataset.fmt === card.settings.format); }); } function setChipsLoading(card){ card.el.querySelectorAll('[data-role="chips"] .chip').forEach(function(btn){ if (!btn.disabled){ var sz = btn.querySelector('.sz'); if (sz) sz.textContent = '…'; var badge = btn.querySelector('.badge'); if (badge) badge.remove(); } }); var outSize = card.el.querySelector('[data-role="outsize"]'); if (outSize) outSize.textContent = '…'; } function recomputeCard(card){ if (card.errored) return; card.busy = true; setChipsLoading(card); var dims = getTargetDims(card); card.outW = dims.w; card.outH = dims.h; var q = QUALITY_STOPS[card.settings.qIndex].q; var jobs = ALL_FORMATS.filter(function(f){ return SUPPORTED[f]; }).map(function(fmt){ return computeBlob(card.img, fmt, q, dims.w, dims.h) .then(function(blob){ return { fmt: fmt, blob: blob }; }) .catch(function(){ return { fmt: fmt, blob: null }; }); }); Promise.all(jobs).then(function(results){ var blobs = {}; results.forEach(function(r){ blobs[r.fmt] = r.blob; }); card.blobs = blobs; card.busy = false; updateCardUI(card); updateAggregate(); }); } function updateCardUI(card){ // update chips var smallestFmt = null, smallestSize = Infinity; Object.keys(card.blobs).forEach(function(fmt){ var blob = card.blobs[fmt]; if (blob && blob.size < smallestSize){ smallestSize = blob.size; smallestFmt = fmt; } }); card.el.querySelectorAll('[data-role="chips"] .chip').forEach(function(btn){ var fmt = btn.dataset.fmt; var blob = card.blobs[fmt]; var sz = btn.querySelector('.sz'); if (!SUPPORTED[fmt]){ if (sz) sz.textContent = 'unsupported'; return; } if (sz) sz.textContent = blob ? formatBytes(blob.size) : 'failed'; var existingBadge = btn.querySelector('.badge'); if (existingBadge) existingBadge.remove(); if (fmt === smallestFmt){ var badge = document.createElement('span'); badge.className = 'badge'; badge.textContent = 'smallest'; btn.appendChild(badge); } }); updateResultPanel(card); } function updateResultPanel(card){ var fmt = card.settings.format; var blob = card.blobs ? card.blobs[fmt] : null; var outEl = card.el.querySelector('[data-role="outsize"]'); var pctEl = card.el.querySelector('[data-role="pct"]'); var dlBtn = card.el.querySelector('[data-role="download"]'); if (card.downloadURL){ URL.revokeObjectURL(card.downloadURL); card.downloadURL = null; } if (!blob){ if (outEl) outEl.textContent = SUPPORTED[fmt] ? '…' : 'unsupported'; if (pctEl) pctEl.textContent = ''; if (dlBtn) dlBtn.disabled = true; return; } card.downloadURL = URL.createObjectURL(blob); if (outEl) outEl.textContent = formatBytes(blob.size); var pct = pctChange(card.originalSize, blob.size); if (pctEl){ pctEl.textContent = (pct >= 0 ? '−' : '+') + Math.abs(pct).toFixed(0) + '%'; pctEl.className = 'pct-badge ' + (pct >= 0 ? 'down' : 'up'); } if (dlBtn) dlBtn.disabled = false; } function removeCard(id){ var card = state.cards.get(id); if (!card) return; if (card.objectURL) URL.revokeObjectURL(card.objectURL); if (card.downloadURL) URL.revokeObjectURL(card.downloadURL); if (card.el && card.el.parentNode) card.el.parentNode.removeChild(card.el); state.cards.delete(id); state.order = state.order.filter(function(x){ return x !== id; }); updateAggregate(); if (state.cards.size === 0) hideGlobalControls(); } // ---------- Aggregate / global controls ---------- function updateAggregate(){ var n = state.cards.size; var totalOrig = 0, totalOut = 0, anyOut = false; state.cards.forEach(function(card){ totalOrig += card.originalSize || 0; var blob = card.blobs ? card.blobs[card.settings.format] : null; if (blob){ totalOut += blob.size; anyOut = true; } else { totalOut += card.originalSize || 0; } }); var el = document.getElementById('totalsText'); if (!el) return; if (n === 0){ el.textContent = '0 images'; return; } var text = n + (n === 1 ? ' image' : ' images') + ' · ' + formatBytes(totalOrig) + ' → ' + formatBytes(totalOut); if (anyOut && totalOrig > 0){ var pct = pctChange(totalOrig, totalOut); text += ' · ' + (pct >= 0 ? '−' : '+') + Math.abs(pct).toFixed(0) + '%'; } el.innerHTML = text; } function showGlobalControls(){ document.getElementById('statsbar').classList.add('show'); } function hideGlobalControls(){ document.getElementById('statsbar').classList.remove('show'); } function applyToAll(){ var qIndex = parseInt(document.getElementById('globalQuality').value, 10); var fmt = document.getElementById('globalFormat').value; state.cards.forEach(function(card){ if (card.errored) return; card.settings.qIndex = qIndex; card.settings.format = fmt; updateQualityLabel(card); updateChipActive(card); var qSlider = card.el.querySelector('[data-role="quality"]'); if (qSlider) qSlider.value = qIndex; recomputeCard(card); }); } function clearAll(){ Array.from(state.order).forEach(function(id){ removeCard(id); }); } function downloadAll(){ if (typeof JSZip === 'undefined'){ alert('The zip library failed to load (check your network connection).'); return; } var zip = new JSZip(); var used = {}; var any = false; state.cards.forEach(function(card){ if (card.errored) return; var fmt = card.settings.format; var blob = card.blobs ? card.blobs[fmt] : null; if (!blob) return; var name = card.base + '.' + EXT[fmt]; if (used[name]){ used[name]++; name = card.base + '-' + used[name] + '.' + EXT[fmt]; } else { used[name] = 1; } zip.file(name, blob); any = true; }); if (!any){ alert('Nothing ready to download yet — wait for processing to finish.'); return; } zip.generateAsync({ type: 'blob' }).then(function(content){ var url = URL.createObjectURL(content); triggerDownload(url, 'Rajmedical-export.zip'); setTimeout(function(){ URL.revokeObjectURL(url); }, 4000); }); } // ---------- File intake ---------- function handleFiles(fileList){ var files = Array.from(fileList).filter(function(f){ return f.type.indexOf('image/') === 0 || /\.(heic|heif|avif)$/i.test(f.name); }); files.forEach(addCard); } function wireDropzone(){ var dz = document.getElementById('dropzone'); var input = document.getElementById('fileInput'); dz.addEventListener('click', function(){ input.click(); }); dz.addEventListener('keydown', function(e){ if (e.key === 'Enter' || e.key === ' '){ e.preventDefault(); input.click(); } }); input.addEventListener('change', function(){ handleFiles(input.files); input.value = ''; }); ['dragenter','dragover'].forEach(function(evt){ dz.addEventListener(evt, function(e){ e.preventDefault(); dz.classList.add('active'); }); }); ['dragleave','drop'].forEach(function(evt){ dz.addEventListener(evt, function(e){ e.preventDefault(); dz.classList.remove('active'); }); }); dz.addEventListener('drop', function(e){ if (e.dataTransfer && e.dataTransfer.files) handleFiles(e.dataTransfer.files); }); } function init(){ Promise.all([ detectFormatSupport('image/webp'), detectFormatSupport('image/avif') ]).then(function(res){ SUPPORTED.webp = res[0]; SUPPORTED.avif = res[1]; defaultFormat = SUPPORTED.webp ? 'webp' : 'jpeg'; document.getElementById('globalFormat').value = defaultFormat; }); wireDropzone(); document.getElementById('applyAllBtn').addEventListener('click', applyToAll); document.getElementById('downloadAllBtn').addEventListener('click', downloadAll); document.getElementById('clearAllBtn').addEventListener('click', clearAll); } document.addEventListener('DOMContentLoaded', init); })();

टिप्पणी छोड़ें

आपका ईमेल पता प्रकाशित नहीं किया जाएगा. आवश्यक फ़ील्ड चिह्नित हैं *

ऊपर स्क्रॉल करें