Image Compressor 2025

0






AI Image Compressor — Centered


AI Image Compressor — Centered

Tool placed in the center of the page. Works offline, client-side. Drag & drop or choose files.

Drag & Drop images here


Notes: The tool is centered in the viewport — if you want it embedded inside an article middle (inline), tell me and I’ll supply that variant.

${escapeHtml(f.file.name)}
Original: ${(f.file.size/1024).toFixed(1)} KB ${f.compressed ? `
Compressed: ${(f.compressed.size/1024).toFixed(1)} KB` : ''}
${f.compressed ? `` : ''}

${f.compressed ? `

Saved: ${((f.file.size - f.compressed.size)/1024).toFixed(1)} KB

` : ''}

`; listEl.appendChild(row); }); }

function removeFile(i){ URL.revokeObjectURL(files[i].preview); files.splice(i,1); render(); }

async function compressOne(i){ const item = files[i]; const quality = parseFloat(qRange.value); const minQuality = parseFloat(minQRange.value); const maxW = parseInt(maxWidthInput.value) || 1600; const fmt = formatSel.value; const targetKB = parseInt(targetKBInput.value) || null; const targetBytes = targetKB ? targetKB*1024 : null; const watermark = watermarkInput.value.trim(); const wmPos = watermarkPos.value;

const img = await loadImage(item.file); let w = img.width, h = img.height; if (w > maxW) { h = Math.round(maxW * (img.height / img.width)); w = maxW; }

const canvas = document.createElement('canvas'); canvas.width = w; canvas.height = h; const ctx = canvas.getContext('2d'); ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = 'high'; ctx.drawImage(img,0,0,w,h);

if (watermark) drawWatermark(ctx, watermark, w, h, wmPos);

// determine mime let mime = 'image/webp'; if (fmt === 'jpeg') mime = 'image/jpeg'; if (fmt === 'png') mime = 'image/png'; if (fmt === 'auto') { try { canvas.toDataURL('image/webp'); mime='image/webp'; } catch { mime='image/jpeg'; } }

// helper const canvasToBlob = (c,m,q)=> new Promise(res=>c.toBlob(res,m,q));

if (!targetBytes) { const blob = await canvasToBlob(canvas,mime,quality); item.compressed = blob; render(); return; }

// target mode: iterate let q = quality; let best = null; while (q >= minQuality) { const b = await canvasToBlob(canvas,mime,q); if (!best || b.size < best.size) best = b; if (b.size <= targetBytes) { item.compressed = b; render(); return; } q = Math.round((q - 0.05)*100)/100; } item.compressed = best; render(); }function drawWatermark(ctx, text, w, h, pos){ const fontSize = Math.max(12, Math.round(w/36)); ctx.font = `${fontSize}px sans-serif`; ctx.textBaseline = 'bottom'; ctx.fillStyle = 'rgba(255,255,255,0.85)'; ctx.strokeStyle = 'rgba(0,0,0,0.55)'; ctx.lineWidth = 2; const metrics = ctx.measureText(text); const textW = metrics.width; const pad = 12; let x = w - textW - pad, y = h - pad; if (pos === 'top-left') { x = pad; y = fontSize + pad; } if (pos === 'top-right') { x = w - textW - pad; y = fontSize + pad; } if (pos === 'bottom-left') { x = pad; y = h - pad; } ctx.strokeText(text, x, y); ctx.fillText(text, x, y); }function loadImage(file){ return new Promise(res => { const img = new Image(); img.onload=()=>res(img); img.onerror=()=>res(img); img.src=URL.createObjectURL(file); }); }

function downloadOne(i){ const blob = files[i].compressed; if(!blob) return alert('No compressed blob yet.'); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = 'compressed-' + files[i].file.name; document.body.appendChild(a); a.click(); a.remove(); }

document.getElementById('compressAllBtn').addEventListener('click', async ()=>{ for(let i=0;i{ files.forEach(f=>URL.revokeObjectURL(f.preview)); files=[]; render(); });

/* small helper to escape text in DOM */ function escapeHtml(s){ return String(s).replace(/[&<>"']/g, m=>({ '&':'&','<':'<','>':'>','"':'"',"'":''' }[m])); }