Sourav Mukherjee
@Design By
100% FREE LIFETIME
100% FREE LIFETIME
Generic selectors
Exact matches only
Search in title
Search in content
Post Type Selectors
Compress Image Ultimate :root { –ciu-primary: #8b5cf6; –ciu-primary-dark: #7c3aed; –ciu-primary-light: #a78bfa; –ciu-secondary: #06b6d4; –ciu-bg-dark: #0f172a; –ciu-bg-card: #1e293b; –ciu-bg-input: #334155; –ciu-bg-hover: #475569; –ciu-text-primary: #f8fafc; –ciu-text-secondary: #cbd5e1; –ciu-text-muted: #94a3b8; –ciu-success: #10b981; –ciu-warning: #f59e0b; –ciu-error: #ef4444; –ciu-info: #3b82f6; –ciu-gradient-primary: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 50%, #06b6d4 100%); –ciu-gradient-card: linear-gradient(145deg, #1e293b 0%, #0f172a 100%); –ciu-gradient-button: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%); –ciu-gradient-success: linear-gradient(135deg, #10b981 0%, #059669 100%); –ciu-shadow-lg: 0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05); –ciu-shadow-glow: 0 0 40px rgba(139,92,246,.3); –ciu-radius-sm: .375rem; –ciu-radius-md: .5rem; –ciu-radius-lg: .75rem; –ciu-radius-xl: 1rem; –ciu-radius-2xl: 1.5rem; –ciu-transition-fast: 150ms ease; –ciu-transition-normal: 250ms ease; } .ciu-container * { margin:0; padding:0; box-sizing:border-box; } .ciu-container { font-family: ‘Inter’, -apple-system, BlinkMacSystemFont, sans-serif; background: var(–ciu-bg-dark); color: var(–ciu-text-primary); line-height: 1.6; min-height: 100vh; width: 100%; overflow-x: hidden; } .ciu-container h1,.ciu-container h2,.ciu-container h3 { font-weight:700; line-height:1.2; } .ciu-container h1 { font-size: clamp(2rem,5vw,3.5rem); background: var(–ciu-gradient-primary); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; } .ciu-container p { color: var(–ciu-text-secondary); } .ciu-wrapper { max-width:1200px; margin:0 auto; padding:2rem; } /* HEADER */ .ciu-header { text-align:center; margin-bottom:3rem; padding:2rem 0; } .ciu-badge { display:inline-flex; align-items:center; gap:.5rem; background:rgba(139,92,246,.1); border:1px solid rgba(139,92,246,.3); padding:.5rem 1rem; border-radius:9999px; font-size:.875rem; color:var(–ciu-primary-light); margin-bottom:1.5rem; animation: ciu-pulse 2s infinite; } @keyframes ciu-pulse { 0%,100%{opacity:1} 50%{opacity:.7} } .ciu-header h1 { margin-bottom:1rem; } .ciu-header-subtitle { font-size:1.125rem; max-width:600px; margin:0 auto; color:var(–ciu-text-muted); } .ciu-formats { display:flex; justify-content:center; gap:1rem; margin-top:2rem; flex-wrap:wrap; } .ciu-format-item { display:flex; align-items:center; gap:.25rem; background:var(–ciu-bg-card); padding:.5rem 1rem; border-radius:var(–ciu-radius-md); color:var(–ciu-text-muted); font-size:.75rem; font-weight:600; text-transform:uppercase; border:1px solid rgba(255,255,255,.1); transition:var(–ciu-transition-normal); } .ciu-format-item:hover { border-color:var(–ciu-primary); color:var(–ciu-primary-light); transform:translateY(-2px); } /* MAIN CARD */ .ciu-main-card { background:var(–ciu-gradient-card); border:1px solid rgba(255,255,255,.1); border-radius:var(–ciu-radius-2xl); padding:3rem; box-shadow:var(–ciu-shadow-lg),var(–ciu-shadow-glow); margin-bottom:3rem; } /* MODE TABS */ .ciu-mode-tabs { display:flex; gap:.5rem; margin:2rem 0; background:var(–ciu-bg-input); padding:.5rem; border-radius:var(–ciu-radius-lg); } .ciu-mode-tab { flex:1; padding:1rem 1.5rem; border:none; background:transparent; color:var(–ciu-text-muted); font-weight:500; border-radius:var(–ciu-radius-md); cursor:pointer; transition:var(–ciu-transition-fast); font-size:.875rem; font-family:inherit; } .ciu-mode-tab:hover { color:var(–ciu-text-primary); } .ciu-mode-tab.ciu-active { background:var(–ciu-gradient-button); color:#fff; box-shadow:0 4px 6px -1px rgba(0,0,0,.1); } /* UPLOAD ZONE */ .ciu-upload-zone { border:2px dashed rgba(139,92,246,.4); border-radius:var(–ciu-radius-xl); padding:3rem; text-align:center; background:rgba(139,92,246,.05); transition:var(–ciu-transition-normal); cursor:pointer; position:relative; overflow:hidden; } .ciu-upload-zone::before { content:”; position:absolute; top:0; left:-100%; width:100%; height:100%; background:linear-gradient(90deg,transparent,rgba(139,92,246,.1),transparent); transition:left .5s; } .ciu-upload-zone:hover::before { left:100%; } .ciu-upload-zone:hover, .ciu-upload-zone.ciu-drag-over { border-color:var(–ciu-primary); background:rgba(139,92,246,.1); transform:scale(1.01); } .ciu-upload-zone.ciu-drag-over { border-color:var(–ciu-success); background:rgba(16,185,129,.1); } .ciu-upload-icon { font-size:4rem; color:var(–ciu-primary); margin-bottom:1rem; animation:ciu-float 3s ease-in-out infinite; } @keyframes ciu-float { 0%,100%{transform:translateY(0)} 50%{transform:translateY(-10px)} } .ciu-upload-text { font-size:1.25rem; font-weight:600; margin-bottom:.5rem; color:var(–ciu-text-primary); } .ciu-upload-subtext { color:var(–ciu-text-muted); font-size:.875rem; } .ciu-file-input { display:none; } /* QUALITY */ .ciu-quality-section { margin:2rem 0; padding:1.5rem; background:var(–ciu-bg-input); border-radius:var(–ciu-radius-lg); } .ciu-section-label { display:flex; justify-content:space-between; align-items:center; font-weight:600; margin-bottom:1rem; color:var(–ciu-text-primary); } .ciu-quality-value { background:var(–ciu-gradient-primary); color:#fff; padding:.25rem 1rem; border-radius:var(–ciu-radius-md); font-size:.875rem; font-weight:700; } .ciu-quality-slider { width:100%; height:8px; border-radius:9999px; background:var(–ciu-bg-dark); outline:none; -webkit-appearance:none; margin-bottom:1rem; cursor:pointer; } .ciu-quality-slider::-webkit-slider-thumb { -webkit-appearance:none; width:24px; height:24px; border-radius:50%; background:var(–ciu-gradient-button); cursor:pointer; box-shadow:0 0 10px rgba(139,92,246,.5); transition:var(–ciu-transition-fast); } .ciu-quality-slider::-webkit-slider-thumb:hover { transform:scale(1.2); } .ciu-quality-slider::-moz-range-thumb { width:24px; height:24px; border-radius:50%; background:var(–ciu-gradient-button); cursor:pointer; border:none; box-shadow:0 0 10px rgba(139,92,246,.5); } .ciu-quality-labels { display:flex; justify-content:space-between; font-size:.75rem; color:var(–ciu-text-muted); } /* OUTPUT FORMAT */ .ciu-output-section { margin:2rem 0; } .ciu-section-title { display:block; font-weight:600; margin-bottom:1rem; color:var(–ciu-text-primary); } .ciu-output-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(100px,1fr)); gap:1rem; } .ciu-output-btn { background:var(–ciu-bg-input); border:2px solid transparent; color:var(–ciu-text-secondary); padding:1rem .5rem; border-radius:var(–ciu-radius-md); cursor:pointer; font-weight:500; transition:var(–ciu-transition-fast); font-size:.875rem; display:flex; flex-direction:column; align-items:center; gap:.25rem; font-family:inherit; } .ciu-output-btn i { font-size:1.25rem; margin-bottom:.25rem; } .ciu-output-btn:hover { border-color:var(–ciu-primary-light); color:var(–ciu-text-primary); } .ciu-output-btn.ciu-selected { background:var(–ciu-gradient-button); border-color:var(–ciu-primary); color:#fff; } /* TARGET SIZE */ .ciu-target-section { margin:2rem 0; } .ciu-target-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(100px,1fr)); gap:1rem; } .ciu-target-btn { background:var(–ciu-bg-input); border:2px solid transparent; color:var(–ciu-text-secondary); padding:1rem; border-radius:var(–ciu-radius-md); cursor:pointer; font-weight:500; transition:var(–ciu-transition-fast); font-size:.875rem; font-family:inherit; } .ciu-target-btn:hover { border-color:var(–ciu-primary-light); color:var(–ciu-text-primary); } .ciu-target-btn.ciu-selected { background:var(–ciu-gradient-button); border-color:var(–ciu-primary); color:#fff; } .ciu-target-custom { grid-column:span 2; display:flex; gap:.5rem; } .ciu-target-custom input { flex:1; background:var(–ciu-bg-input); border:2px solid transparent; color:var(–ciu-text-primary); padding:1rem; border-radius:var(–ciu-radius-md); font-size:.875rem; font-family:inherit; } .ciu-target-custom input:focus { outline:none; border-color:var(–ciu-primary); } /* FILE LIST */ .ciu-file-list { margin-top:2rem; } .ciu-file-item { display:flex; align-items:center; gap:1rem; background:var(–ciu-bg-input); padding:1rem; border-radius:var(–ciu-radius-lg); margin-bottom:1rem; animation:ciu-slideIn .3s ease; position:relative; overflow:hidden; } @keyframes ciu-slideIn { from{opacity:0;transform:translateX(-20px)} to{opacity:1;transform:translateX(0)} } .ciu-file-preview { width:80px; height:80px; border-radius:var(–ciu-radius-md); object-fit:cover; border:2px solid rgba(255,255,255,.1); background:var(–ciu-bg-dark); cursor:pointer; flex-shrink:0; } .ciu-file-icon { width:80px; height:80px; background:rgba(139,92,246,.2); border-radius:var(–ciu-radius-md); display:flex; align-items:center; justify-content:center; color:var(–ciu-primary); font-size:2rem; flex-shrink:0; } .ciu-file-info { flex:1; min-width:0; } .ciu-file-name { font-weight:500; color:var(–ciu-text-primary); margin-bottom:.25rem; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; } .ciu-file-meta { display:flex; gap:1rem; font-size:.75rem; color:var(–ciu-text-muted); flex-wrap:wrap; } .ciu-file-sizes { display:flex; align-items:center; gap:.5rem; margin-top:.5rem; flex-wrap:wrap; } .ciu-size-original { color:var(–ciu-text-muted); text-decoration:line-through; } .ciu-size-arrow { color:var(–ciu-primary-light); } .ciu-size-compressed { color:var(–ciu-success); font-weight:600; } .ciu-size-savings { background:var(–ciu-success); color:#fff; padding:.25rem .5rem; border-radius:var(–ciu-radius-sm); font-size:.75rem; font-weight:600; } .ciu-file-status { display:flex; align-items:center; gap:.5rem; flex-shrink:0; flex-direction:column; } .ciu-status-badge { padding:.25rem .5rem; border-radius:var(–ciu-radius-sm); font-size:.75rem; font-weight:500; white-space:nowrap; } .ciu-status-pending { background:rgba(245,158,11,.2); color:var(–ciu-warning); } .ciu-status-processing { background:rgba(59,130,246,.2); color:var(–ciu-info); } .ciu-status-completed { background:rgba(16,185,129,.2); color:var(–ciu-success); } .ciu-status-error { background:rgba(239,68,68,.2); color:var(–ciu-error); } .ciu-file-remove { background:none; border:none; color:var(–ciu-text-muted); cursor:pointer; padding:.5rem; border-radius:var(–ciu-radius-sm); transition:var(–ciu-transition-fast); flex-shrink:0; } .ciu-file-remove:hover { color:var(–ciu-error); background:rgba(239,68,68,.1); } .ciu-file-download { display:none; background:none; border:1px solid var(–ciu-success); color:var(–ciu-success); cursor:pointer; padding:.3rem .7rem; border-radius:var(–ciu-radius-sm); font-size:.7rem; font-weight:600; font-family:inherit; align-items:center; gap:.25rem; transition:var(–ciu-transition-fast); white-space:nowrap; } .ciu-file-download.ciu-visible { display:flex; } .ciu-file-download:hover { background:var(–ciu-success); color:#fff; } .ciu-live-preview { margin-top:.5rem; padding:.5rem; background:rgba(16,185,129,.1); border-radius:var(–ciu-radius-md); border-left:3px solid var(–ciu-success); } .ciu-live-stats { display:flex; justify-content:space-between; align-items:center; font-size:.75rem; } .ciu-live-label { color:var(–ciu-text-muted); } .ciu-live-value { color:var(–ciu-success); font-weight:600; } /* PROGRESS */ .ciu-progress-container { margin-top:1rem; } .ciu-progress-bar { height:8px; background:var(–ciu-bg-input); border-radius:9999px; overflow:hidden; position:relative; } .ciu-progress-fill { height:100%; background:var(–ciu-gradient-primary); border-radius:9999px; transition:width .3s ease; position:relative; overflow:hidden; } .ciu-progress-fill::after { content:”; position:absolute; top:0; left:0; right:0; bottom:0; background:linear-gradient(90deg,transparent,rgba(255,255,255,.3),transparent); animation:ciu-shimmer 1.5s infinite; } @keyframes ciu-shimmer { 0%{transform:translateX(-100%)} 100%{transform:translateX(100%)} } .ciu-progress-text { text-align:center; margin-top:.5rem; font-size:.875rem; color:var(–ciu-text-muted); } .ciu-batch-progress { margin-top:1rem; padding:1rem; background:var(–ciu-bg-input); border-radius:var(–ciu-radius-md); } .ciu-batch-header { display:flex; justify-content:space-between; margin-bottom:.5rem; font-size:.875rem; } .ciu-batch-count { color:var(–ciu-text-muted); } .ciu-batch-percentage { color:var(–ciu-primary-light); font-weight:600; } /* ACTIONS */ .ciu-actions { display:flex; gap:1rem; margin-top:2rem; flex-wrap:wrap; } .ciu-btn { flex:1; min-width:200px; padding:1rem 2rem; border:none; border-radius:var(–ciu-radius-lg); font-weight:600; font-size:1rem; cursor:pointer; transition:var(–ciu-transition-normal); display:flex; align-items:center; justify-content:center; gap:.5rem; font-family:inherit; } .ciu-btn-primary { background:var(–ciu-gradient-button); color:#fff; box-shadow:0 4px 14px rgba(139,92,246,.4); } .ciu-btn-primary:hover:not(:disabled) { transform:translateY(-2px); box-shadow:0 6px 20px rgba(139,92,246,.5); } .ciu-btn-primary:disabled { opacity:.6; cursor:not-allowed; } .ciu-btn-secondary { background:var(–ciu-bg-input); color:var(–ciu-text-primary); border:1px solid rgba(255,255,255,.1); } .ciu-btn-secondary:hover { background:var(–ciu-bg-hover); } .ciu-btn-success { background:var(–ciu-gradient-success); color:#fff; box-shadow:0 4px 14px rgba(16,185,129,.4); } .ciu-btn-success:hover { transform:translateY(-2px); box-shadow:0 6px 20px rgba(16,185,129,.5); } /* RESULTS */ .ciu-results { margin-top:2rem; padding:2rem; background:rgba(16,185,129,.1); border:1px solid rgba(16,185,129,.3); border-radius:var(–ciu-radius-xl); display:none; } .ciu-results.ciu-visible { display:block; animation:ciu-fadeIn .5s ease; } @keyframes ciu-fadeIn { from{opacity:0;transform:translateY(10px)} to{opacity:1;transform:translateY(0)} } .ciu-results-header { display:flex; align-items:center; gap:1rem; margin-bottom:1.5rem; } .ciu-results-icon { width:56px; height:56px; background:var(–ciu-success); border-radius:50%; display:flex; align-items:center; justify-content:center; color:#fff; font-size:1.5rem; flex-shrink:0; } .ciu-results-title { font-size:1.25rem; font-weight:700; color:var(–ciu-text-primary); } .ciu-results-subtitle { color:var(–ciu-text-muted); font-size:.875rem; } .ciu-stats-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); gap:1rem; margin-bottom:1.5rem; } .ciu-stat-card { background:var(–ciu-bg-card); padding:1.5rem; border-radius:var(–ciu-radius-lg); text-align:center; } .ciu-stat-value { font-size:1.5rem; font-weight:700; color:var(–ciu-primary-light); margin-bottom:.25rem; } .ciu-stat-label { font-size:.75rem; color:var(–ciu-text-muted); text-transform:uppercase; letter-spacing:.05em; } .ciu-comparison { margin-top:1rem; display:grid; grid-template-columns:1fr 1fr; gap:1rem; } .ciu-comparison-item { text-align:center; padding:1rem; background:var(–ciu-bg-card); border-radius:var(–ciu-radius-lg); } .ciu-comparison-label { font-size:.75rem; color:var(–ciu-text-muted); margin-bottom:.5rem; text-transform:uppercase; } .ciu-comparison-size { font-size:1.25rem; font-weight:700; color:var(–ciu-text-primary); } .ciu-comparison-size.ciu-reduced { color:var(–ciu-success); } /* FEATURES */ .ciu-features { display:grid; grid-template-columns:repeat(auto-fit,minmax(280px,1fr)); gap:2rem; margin-top:3rem; } .ciu-feature-card { background:var(–ciu-gradient-card); border:1px solid rgba(255,255,255,.1); border-radius:var(–ciu-radius-xl); padding:2rem; transition:var(–ciu-transition-normal); } .ciu-feature-card:hover { transform:translateY(-4px); box-shadow:var(–ciu-shadow-lg); border-color:rgba(139,92,246,.3); } .ciu-feature-icon { width:56px; height:56px; background:rgba(139,92,246,.1); border-radius:var(–ciu-radius-lg); display:flex; align-items:center; justify-content:center; color:var(–ciu-primary); font-size:1.5rem; margin-bottom:1.5rem; } .ciu-feature-title { font-size:1.125rem; font-weight:600; margin-bottom:.5rem; color:var(–ciu-text-primary); } .ciu-feature-desc { color:var(–ciu-text-muted); font-size:.875rem; line-height:1.6; } /* SECURITY */ .ciu-security-badge { display:flex; align-items:center; justify-content:center; gap:1rem; margin-top:2rem; padding:1.5rem; background:rgba(16,185,129,.1); border-radius:var(–ciu-radius-lg); color:var(–ciu-success); font-size:.875rem; } /* TOAST */ .ciu-toast-container { position:fixed; bottom:2rem; right:2rem; z-index:9999; display:flex; flex-direction:column; gap:1rem; } .ciu-toast { background:var(–ciu-bg-card); border:1px solid rgba(255,255,255,.1); border-radius:var(–ciu-radius-lg); padding:1rem 1.5rem; box-shadow:var(–ciu-shadow-lg); display:flex; align-items:center; gap:1rem; min-width:300px; animation:ciu-toastIn .3s ease; transition:opacity .3s,transform .3s; } @keyframes ciu-toastIn { from{opacity:0;transform:translateX(100%)} to{opacity:1;transform:translateX(0)} } .ciu-toast-success { border-left:4px solid var(–ciu-success); } .ciu-toast-error { border-left:4px solid var(–ciu-error); } .ciu-toast-warning { border-left:4px solid var(–ciu-warning); } /* MODAL */ .ciu-modal { display:none; position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,.9); z-index:10000; align-items:center; justify-content:center; padding:2rem; } .ciu-modal.ciu-visible { display:flex; } .ciu-modal-content { max-width:90%; max-height:90%; border-radius:var(–ciu-radius-lg); box-shadow:var(–ciu-shadow-lg); } .ciu-modal-close { position:absolute; top:2rem; right:2rem; background:none; border:none; color:#fff; font-size:2rem; cursor:pointer; width:48px; height:48px; display:flex; align-items:center; justify-content:center; border-radius:50%; transition:var(–ciu-transition-fast); } .ciu-modal-close:hover { background:rgba(255,255,255,.1); } .ciu-hidden { display:none !important; } /* RESPONSIVE */ @media(max-width:768px){ .ciu-wrapper{padding:1rem} .ciu-main-card{padding:1.5rem} .ciu-target-grid{grid-template-columns:repeat(2,1fr)} .ciu-target-custom{grid-column:span 2} .ciu-output-grid{grid-template-columns:repeat(3,1fr)} .ciu-actions{flex-direction:column} .ciu-btn{width:100%} .ciu-file-item{flex-wrap:wrap} .ciu-file-preview,.ciu-file-icon{width:60px;height:60px} .ciu-comparison{grid-template-columns:1fr} .ciu-toast-container{right:1rem;bottom:1rem} .ciu-toast{min-width:unset;max-width:calc(100vw – 2rem)} } @media(max-width:480px){ .ciu-upload-icon{font-size:3rem} .ciu-upload-text{font-size:1rem} .ciu-mode-tabs{flex-direction:column} .ciu-file-sizes{flex-direction:column;align-items:flex-start;gap:.25rem} .ciu-output-grid{grid-template-columns:repeat(2,1fr)} }
Real Canvas Compression + Live Preview

Compress Image Ultimate

Compress any image format instantly with real-time size estimation. Support for JPG, PNG, WebP, GIF, SVG, BMP, TIFF. Drag, preview, compress, download. Learn more about image compressor online.

JPG
PNG
WebP
GIF
SVG
BMP
TIFF
Drag & Drop your images here
or click to browse • Max 25 MB each • Up to 50 images
Maximum Compression Best Quality
Processing 0 of 0 0%
Preparing…
Compression Complete!
Your images have been optimized successfully
Original Size
0 KB
Compressed Size
0 KB
100% Private: All image processing happens locally in your browser. Your images never leave your device.

Live Size Preview

See estimated compressed file sizes in real-time before processing. Adjust quality and instantly see the impact on file size.

Real Canvas Compression

Actual browser Canvas API compression — not a simulation. True JPG/PNG/WebP output with genuine quality reduction and format conversion.

Bulk Processing + ZIP

Process up to 50 images simultaneously. Download individually per file or all at once as a ZIP archive with one click.

Target Size Mode

Set a specific target size (e.g. 100 KB) and the engine iterates quality via binary search until your output hits the exact target.

Preview
(function () { ‘use strict’; /* ═══════════ STATE ═══════════ */ const state = { files: [], mode: ‘single’, quality: 80, targetSize: 0, // KB, 0 = auto outputFormat: ‘original’, isProcessing: false, processedFiles: [] // sparse array indexed same as state.files }; /* ═══════════ DOM ═══════════ */ const $ = id => document.getElementById(id); const el = { uploadZone: $(‘ciuUploadZone’), fileInput: $(‘ciuFileInput’), fileList: $(‘ciuFileList’), modeTabs: document.querySelectorAll(‘.ciu-mode-tab’), qualitySlider: $(‘ciuQualitySlider’), qualityValue: $(‘ciuQualityValue’), outputBtns: document.querySelectorAll(‘.ciu-output-btn’), targetBtns: document.querySelectorAll(‘.ciu-target-btn’), customSizeIn: $(‘ciuCustomSize’), customSizeBtn: $(‘ciuCustomSizeBtn’), compressBtn: $(‘ciuCompressBtn’), downloadBtn: $(‘ciuDownloadBtn’), clearBtn: $(‘ciuClearBtn’), progCont: $(‘ciuProgressContainer’), progFill: $(‘ciuProgressFill’), progText: $(‘ciuProgressText’), batchProg: $(‘ciuBatchProgress’), batchFill: $(‘ciuBatchProgressFill’), batchCount: document.querySelector(‘.ciu-batch-count’), batchPct: document.querySelector(‘.ciu-batch-percentage’), results: $(‘ciuResults’), statsGrid: $(‘ciuStatsGrid’), origTotal: $(‘ciuOriginalTotal’), compTotal: $(‘ciuCompressedTotal’), toastCont: $(‘ciuToastContainer’), modal: $(‘ciuModal’), modalImg: $(‘ciuModalImage’), modalClose: $(‘ciuModalClose’) }; /* ═══════════ UTILITIES ═══════════ */ const fmtSize = b => { if (!b || b { const icons = { success:’fa-check-circle’, error:’fa-exclamation-circle’, warning:’fa-exclamation-triangle’ }; const t = document.createElement(‘div’); t.className = `ciu-toast ciu-toast-${type}`; t.innerHTML = `${msg}`; el.toastCont.appendChild(t); setTimeout(() => { t.style.opacity = ‘0’; t.style.transform = ‘translateX(100%)’; setTimeout(() => t.remove(), 350); }, 3200); }; /* ═══════════ IMAGE HELPERS ═══════════ */ // Load file → HTMLImageElement via FileReader (base64 data URL). // Using FileReader avoids failures with non-ASCII filenames (Bengali, Arabic, CJK, etc.) // because the image src never contains the filename — only raw base64 data. // Also caches the data URL on the file object for thumbnail display. const loadImg = file => new Promise((res, rej) => { if (file._dataUrl) { // Already read — reuse cached data URL const img = new Image(); img.onload = () => res(img); img.onerror = rej; img.src = file._dataUrl; return; } const reader = new FileReader(); reader.onload = e => { file._dataUrl = e.target.result; // cache for thumbnails & previews const img = new Image(); img.onload = () => res(img); img.onerror = rej; img.src = file._dataUrl; }; reader.onerror = rej; reader.readAsDataURL(file); }); // Canvas compress → Blob const canvasBlob = (img, mimeOut, q) => new Promise(resolve => { const c = document.createElement(‘canvas’); c.width = img.naturalWidth || img.width; c.height = img.naturalHeight || img.height; const ctx = c.getContext(‘2d’); if (mimeOut === ‘image/jpeg’) { ctx.fillStyle = ‘#fff’; ctx.fillRect(0,0,c.width,c.height); } ctx.drawImage(img, 0, 0); c.toBlob(b => resolve(b), mimeOut, q / 100); }); // Binary-search quality to hit targetKB const compressToTarget = async (img, mimeOut, targetKB) => { let lo = 1, hi = 95, best = null; for (let n = 0; n < 14; n++) { const mid = Math.round((lo + hi) / 2); const blob = await canvasBlob(img, mimeOut, mid); if (blob.size / 1024 hi) break; } return best || await canvasBlob(img, mimeOut, 1); }; // Resolve output mime from format setting + file const resolveMime = (file, fmt) => { if (fmt === ‘png’) return ‘image/png’; if (fmt === ‘webp’) return ‘image/webp’; if (fmt === ‘jpeg’) return ‘image/jpeg’; // “original” — keep png/webp, convert everything else to jpeg if (file.type === ‘image/png’) return ‘image/png’; if (file.type === ‘image/webp’) return ‘image/webp’; return ‘image/jpeg’; }; const mimeExt = { ‘image/jpeg’:’jpg’,’image/png’:’png’,’image/webp’:’webp’ }; const getExt = m => mimeExt[m] || ‘jpg’; const origExt = f => (f.name.split(‘.’).pop() || ‘img’).toUpperCase(); /* ═══════════ LIVE ESTIMATE ═══════════ */ const estimate = (bytes, q, fmt) => { const r = q / 100; let f = r>=.9?.93:r>=.8?.72:r>=.7?.57:r>=.6?.43:r>=.5?.33:r>=.3?.22:.13; if (fmt===’png’) f *= 1.25; if (fmt===’webp’) f *= 0.75; return Math.max(512, Math.floor(bytes * f)); }; /* ═══════════ FILE MANAGEMENT ═══════════ */ const handleFiles = async rawFiles => { const arr = Array.from(rawFiles); const valid = []; for (const f of arr) { if (!supportedMime.has(f.type) && !supportedExt.test(f.name)) { toast(`”${f.name}” not supported`,’error’); continue; } if (f.size > 25*1024*1024) { toast(`”${f.name}” exceeds 25 MB`,’error’); continue; } valid.push(f); } if (!valid.length) return; if (state.mode === ‘single’) { state.files = [valid[0]]; } else { const slots = 50 – state.files.length; if (valid.length > slots) toast(`Max 50 files — added first ${slots}`,’warning’); state.files = […state.files, …valid.slice(0, slots)]; } // Load dimensions in parallel (loadImg now uses FileReader data URL) await Promise.all(state.files.map(async f => { if (f._dims) return; try { const img = await loadImg(f); f._dims = { w: img.naturalWidth, h: img.naturalHeight }; } catch { f._dims = { w:0, h:0 }; } })); toast(`${valid.length} image${valid.length>1?’s’:”} added`); render(); updateBtn(); }; const removeFile = idx => { state.files.splice(idx, 1); state.processedFiles.splice(idx, 1); render(); updateBtn(); toast(‘Image removed’,’warning’); }; /* ═══════════ RENDER FILE LIST ═══════════ */ const render = () => { if (!state.files.length) { el.fileList.classList.add(‘ciu-hidden’); return; } el.fileList.classList.remove(‘ciu-hidden’); el.fileList.innerHTML = state.files.map((f, i) => { const isImg = f.type.startsWith(‘image/’); const prevUrl = isImg ? (f._dataUrl || null) : null; const pf = state.processedFiles[i]; const est = estimate(f.size, state.quality, state.outputFormat); const savPct = Math.max(0, ((f.size-est)/f.size*100)).toFixed(0); const dims = f._dims ? `${f._dims.w}×${f._dims.h}` : ‘…’; return `
${isImg ? `preview` : `
`}
${f.name}
${dims} ${origExt(f)}
${fmtSize(f.size)} ${pf ? fmtSize(pf.blob.size) : ‘~’+fmtSize(est)} ${pf ? ‘-‘+((f.size-pf.blob.size)/f.size*100).toFixed(1)+’%’ : ‘-‘+savPct+’%’}
${!state.isProcessing && !pf ? `
Live estimate at ${state.quality}% quality ~${fmtSize(est)} expected
` : ”}
${pf ? ‘Done’ : ‘Pending’} ${pf ? `` : ”}
`; }).join(”); }; const updateEstimates = () => { state.files.forEach((f, i) => { if (state.processedFiles[i]) return; // already processed, don’t overwrite real values const est = estimate(f.size, state.quality, state.outputFormat); const sav = Math.max(0, ((f.size-est)/f.size*100)).toFixed(0); const c = document.getElementById(`cs${i}`); const s = document.getElementById(`sv${i}`); const l = document.getElementById(`lv${i}`); if (c) c.textContent = ‘~’+fmtSize(est); if (s) s.textContent = ‘-‘+sav+’%’; if (l) l.textContent = `~${fmtSize(est)} expected`; }); }; const updateBtn = () => { el.compressBtn.disabled = !state.files.length || state.isProcessing; }; /* ═══════════ REAL COMPRESSION ENGINE ═══════════ */ const compressAll = async () => { if (!state.files.length || state.isProcessing) return; state.isProcessing = true; state.processedFiles = []; updateBtn(); el.progCont.classList.remove(‘ciu-hidden’); el.batchProg.classList.remove(‘ciu-hidden’); el.results.classList.remove(‘ciu-visible’); el.downloadBtn.classList.add(‘ciu-hidden’); const total = state.files.length; for (let i = 0; i 0) { blob = await compressToTarget(img, mimeOut, state.targetSize); } else { blob = await canvasBlob(img, mimeOut, state.quality); } } const ext = file.type === ‘image/svg+xml’ ? ‘svg’ : getExt(mimeOut); const base = file.name.replace(/\.[^/.]+$/, ”); const outName = `compressed_${base}.${ext}`; state.processedFiles[i] = { blob, name: outName, originalSize: file.size }; // Update the file row if (stEl) { stEl.className=’ciu-status-badge ciu-status-completed’; stEl.textContent=’Done’; } const csEl = document.getElementById(`cs${i}`); const svEl = document.getElementById(`sv${i}`); const actSav = ((file.size – blob.size) / file.size * 100).toFixed(1); if (csEl) csEl.textContent = fmtSize(blob.size); if (svEl) svEl.textContent = ‘-‘+actSav+’%’; // Inject per-file download button const row = el.fileList.querySelector(`[data-index=”${i}”]`); if (row) { const statusDiv = row.querySelector(‘.ciu-file-status’); if (statusDiv && !statusDiv.querySelector(‘.ciu-file-download’)) { const btn = document.createElement(‘button’); btn.className = ‘ciu-file-download ciu-visible’; btn.innerHTML = ‘ Save’; btn.onclick = () => window._ciu.dlSingle(i); statusDiv.appendChild(btn); } // Remove live estimate bar const lp = row.querySelector(‘.ciu-live-preview’); if (lp) lp.remove(); } } catch (err) { console.error(‘Compress error for’, file.name, err); if (stEl) { stEl.className=’ciu-status-badge ciu-status-error’; stEl.textContent=’Error’; } state.processedFiles[i] = null; toast(`Failed: ${file.name}`, ‘error’); } // Yield for UI repaint await new Promise(r => setTimeout(r, 20)); } el.batchFill.style.width = ‘100%’; el.progFill.style.width = ‘100%’; el.progText.textContent = ‘Compression complete!’; el.batchCount.textContent = `Processed ${total} of ${total}`; el.batchPct.textContent = ‘100%’; state.isProcessing = false; updateBtn(); setTimeout(() => { el.progCont.classList.add(‘ciu-hidden’); el.batchProg.classList.add(‘ciu-hidden’); showSummary(); el.downloadBtn.classList.remove(‘ciu-hidden’); toast(‘All images compressed! 🎉’); }, 400); }; /* ═══════════ RESULTS SUMMARY ═══════════ */ const showSummary = () => { const done = state.processedFiles.filter(Boolean); if (!done.length) return; const totOrig = done.reduce((s,f) => s+f.originalSize, 0); const totComp = done.reduce((s,f) => s+f.blob.size, 0); const saved = ((totOrig-totComp)/totOrig*100).toFixed(1); el.statsGrid.innerHTML = `
${done.length}
Processed
${fmtSize(totOrig)}
Original
${fmtSize(totComp)}
Compressed
${saved}%
Saved
`; el.origTotal.textContent = fmtSize(totOrig); el.compTotal.textContent = fmtSize(totComp); el.results.classList.add(‘ciu-visible’); }; /* ═══════════ DOWNLOADS ═══════════ */ const triggerDl = (blob, name) => { const url = URL.createObjectURL(blob); const a = document.createElement(‘a’); a.href = url; a.download = name; document.body.appendChild(a); a.click(); document.body.removeChild(a); setTimeout(() => URL.revokeObjectURL(url), 1000); }; const dlAll = async () => { const done = state.processedFiles.filter(Boolean); if (!done.length) { toast(‘Nothing compressed yet’,’warning’); return; } if (done.length === 1) { triggerDl(done[0].blob, done[0].name); toast(‘Downloaded!’); return; } toast(‘Building ZIP archive…’); try { if (typeof JSZip === ‘undefined’) throw new Error(‘JSZip not loaded’); const zip = new JSZip(); done.forEach(f => zip.file(f.name, f.blob)); const zBlob = await zip.generateAsync({ type:’blob’, compression:’DEFLATE’, compressionOptions:{level:6} }); triggerDl(zBlob, ‘compressed_images.zip’); toast(‘ZIP downloaded!’); } catch (e) { console.warn(‘ZIP failed, downloading individually:’, e); done.forEach((f,i) => setTimeout(() => triggerDl(f.blob, f.name), i*350)); toast(‘Downloading files individually…’,’warning’); } }; const dlSingle = idx => { const f = state.processedFiles[idx]; if (!f) { toast(‘Not processed yet’,’warning’); return; } triggerDl(f.blob, f.name); toast(`Downloading ${f.name}`); }; /* ═══════════ CLEAR ═══════════ */ const clearAll = () => { if (state.isProcessing) { toast(‘Processing in progress…’,’warning’); return; } state.files = []; state.processedFiles = []; render(); el.results.classList.remove(‘ciu-visible’); el.downloadBtn.classList.add(‘ciu-hidden’); el.progCont.classList.add(‘ciu-hidden’); el.batchProg.classList.add(‘ciu-hidden’); el.progFill.style.width = ‘0%’; el.batchFill.style.width = ‘0%’; updateBtn(); toast(‘Cleared’,’warning’); }; /* ═══════════ MODAL ═══════════ */ const showPreview = idx => { el.modalImg.src = state.files[idx]._dataUrl || ”; el.modal.classList.add(‘ciu-visible’); }; const closeModal = () => { el.modal.classList.remove(‘ciu-visible’); el.modalImg.src = ”; }; /* ═══════════ EVENT WIRING ═══════════ */ // Upload el.uploadZone.addEventListener(‘click’, () => el.fileInput.click()); el.uploadZone.addEventListener(‘dragover’, e => { e.preventDefault(); el.uploadZone.classList.add(‘ciu-drag-over’); }); el.uploadZone.addEventListener(‘dragleave’, () => el.uploadZone.classList.remove(‘ciu-drag-over’)); el.uploadZone.addEventListener(‘drop’, e => { e.preventDefault(); el.uploadZone.classList.remove(‘ciu-drag-over’); handleFiles(e.dataTransfer.files); }); el.fileInput.addEventListener(‘change’, e => { handleFiles(e.target.files); e.target.value=”; }); // Mode tabs el.modeTabs.forEach(tab => tab.addEventListener(‘click’, () => { el.modeTabs.forEach(t => t.classList.remove(‘ciu-active’)); tab.classList.add(‘ciu-active’); state.mode = tab.dataset.mode; if (state.files.length) clearAll(); toast(`${state.mode===’single’?’Single’:’Bulk’} mode`); })); // Quality el.qualitySlider.addEventListener(‘input’, e => { state.quality = +e.target.value; el.qualityValue.textContent = state.quality + ‘%’; updateEstimates(); }); // Output format el.outputBtns.forEach(btn => btn.addEventListener(‘click’, () => { el.outputBtns.forEach(b => b.classList.remove(‘ciu-selected’)); btn.classList.add(‘ciu-selected’); state.outputFormat = btn.dataset.format; updateEstimates(); })); // Target size presets el.targetBtns.forEach(btn => { if (btn.id === ‘ciuCustomSizeBtn’) return; btn.addEventListener(‘click’, () => { el.targetBtns.forEach(b => b.classList.remove(‘ciu-selected’)); btn.classList.add(‘ciu-selected’); state.targetSize = parseInt(btn.dataset.size) || 0; el.customSizeIn.value = ”; updateEstimates(); }); }); // Custom size el.customSizeBtn.addEventListener(‘click’, () => { const v = parseInt(el.customSizeIn.value); if (v > 0) { el.targetBtns.forEach(b => b.classList.remove(‘ciu-selected’)); el.customSizeBtn.classList.add(‘ciu-selected’); state.targetSize = v; updateEstimates(); toast(`Target: ${v} KB`); } else { toast(‘Enter a valid KB value’,’error’); } }); el.customSizeIn.addEventListener(‘keydown’, e => { if (e.key===’Enter’) el.customSizeBtn.click(); }); // Main actions el.compressBtn.addEventListener(‘click’, compressAll); el.downloadBtn.addEventListener(‘click’, dlAll); el.clearBtn.addEventListener(‘click’, clearAll); // Modal el.modalClose.addEventListener(‘click’, closeModal); el.modal.addEventListener(‘click’, e => { if (e.target===el.modal) closeModal(); }); document.addEventListener(‘keydown’, e => { if (e.key===’Escape’) closeModal(); }); /* ═══════════ GLOBAL API (for inline onclick) ═══════════ */ window._ciu = { remove: removeFile, preview: showPreview, dlSingle }; console.log(‘Compress Image Ultimate v2.0 — Real compression engine ready’); })();
allbesttool.com
Latest posts by allbesttool.com (see all)