1<!DOCTYPE html> 2<html lang="en"> 3 4<head> 5 <meta charset="UTF-8"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <meta name="yandex-verification" content="7b9d2a6b6a23c52f" /> 8 <meta name="baidu-site-verification" content="codeva-5mO3i5ot2y" /> 9 <title>Free Image Compressor â Private, Fast & No Upload | ImgMin Pro</title> 10 <meta name="description" 11 content="Compress JPG, PNG and WebP images directly in your browser. No upload, no account, batch processing and target file size controls. Free with ImgMin Pro."> 12 <meta name="keywords" 13 content="free image compressor, online image compressor, compress image without uploading, browser-based image compressor, bulk image compressor, ImgMin Pro"> 14 15 <!-- Google tag (gtag.js) --> 16
16<script> 17 (function () { 18 function loadGtag() { 19 var s = document.createElement('script'); 20 s.async = true; 21 s.src = 'https://www.googletagmanager.com/gtag/js?id=G-4ZERSTHF27'; 22 document.head.appendChild(s); 23 } 24 if (document.readyState === 'complete') { 25 loadGtag(); 26 } else { 27 window.addEventListener('load', loadGtag); 28 } 29 })(); 30 </script>
30 31
31<script> 32
vendor: 155 bytes, lines 32-36
32window.dataLayer = window.dataLayer || []; 33 function gtag() { dataLayer.push(arguments); } 34 gtag('js', new Date()); 35 36 gtag('config', '
36G-4ZERSTHF27
vendor: 13 bytes, lines 36-38
36'); 37 38
38// Extension acquisition loop: track visits driven by Chrome extension 39 (function () { 40 try { 41 var p = new URLSearchParams(location.search); 42 if (p.get('source') === 'ext') { 43 gtag('event', 'ext_visit', { 44 medium: p.get('utm_medium') || 'unknown', 45 path: location.pathname, 46 }); 47 } 48 } catch (e) { /* noop */ } 49 })(); 50 </script>
50 51 52 <!-- Favicon --> 53 <link rel="icon" type="image/png" sizes="48x48" href="./favicon-48x48.png"> 54 <link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"> 55 <link rel="icon" type="image/png" sizes="16x16" href="./favicon-16x16.png"> 56 <link rel="apple-touch-icon" sizes="180x180" href="./apple-touch-icon.png"> 57 <link rel="manifest" href="./manifest.json"> 58 <meta name="theme-color" content="#6C5CE7"> 59 60 <!-- Canonical URL --> 61 <link rel="canonical" href="https://imgmin.pro/"> 62 63 <!-- Hreflang for multi-language support --> 64 <link rel="alternate" hreflang="zh-CN" href="https://imgmin.pro/zh/"> 65 <link rel="alternate" hreflang="zh-Hant" href="https://imgmin.pro/zh-hant/"> 66 <link rel="alternate" hreflang="en-US" href="https://imgmin.pro/"> 67 <link rel="alternate" hreflang="x-default" href="https://imgmin.pro/"> 68 69 <!-- Open Graph / Facebook --> 70 <meta property="og:type" content="website"> 71 <meta property="og:url" content="https://imgmin.pro/"> 72 <meta property="og:title" content="Free Image Compressor â No Upload Required | ImgMin Pro"> 73 <meta property="og:description" content="Compress JPG, PNG & WebP in your browser â no upload, no server, 100% private. Free image compressor."> 74 <meta property="og:image" content="https://imgmin.pro/android-chrome-512x512.png"> 75 <meta property="og:site_name" content="ImgMin Pro"> 76 77 <!-- Open Graph locale --> 78 <meta property="og:locale" content="en_US"> 79 <meta property="og:locale:alternate" content="en_US"> 80 <meta property="og:locale:alternate" content="fr_FR"> 81 <meta property="og:locale:alternate" content="de_DE"> 82 <meta property="og:locale:alternate" content="es_ES"> 83 <meta property="og:locale:alternate" content="ja_JP"> 84 85 <!-- Twitter --> 86 <meta name="twitter:card" content="summary_large_image"> 87 <meta name="twitter:site" content="@ai_pmer"> 88 <meta name="twitter:creator" content="@ai_pmer"> 89 <meta name="twitter:title" content="Free Image Compressor â No Upload Required | ImgMin Pro"> 90 <meta name="twitter:description" content="Compress JPG, PNG & WebP in your browser â no upload, no server, 100% private."> 91 <meta name="twitter:image" content="https://imgmin.pro/android-chrome-512x512.png"> 92 93 <!-- Preconnect hints --> 94 <link rel="preconnect" href="https://www.googletagmanager.com"> 95 <link rel="preconnect" href="https://cdnjs.cloudflare.com"> 96 97 <!-- JSON-LD Structured Data for SEO --> 98
98<script type="application/ld+json"> 99 { 100 "@context": "https://schema.org", 101 "@type": "WebApplication", 102 "name": "ImgMin Pro", 103 "url": "https://imgmin.pro", 104 "description": "Private image optimization toolkit that runs entirely in your browser. Compress, convert and resize JPG, PNG and WebP images with no upload.", 105 "applicationCategory": "UtilitiesApplication", 106 "operatingSystem": "Any", 107 "offers": { 108 "@type": "Offer", 109 "price": "0", 110 "priceCurrency": "USD" 111 }, 112 "featureList": ["JPG compression", "PNG compression", "WebP compression", "Batch processing", "Client-side only", "No data upload"], 113 "browserRequirements": "Requires JavaScript. Works in all modern browsers.", 114 "creator": { 115 "@type": "Organization", 116 "name": "ImgMin Pro" 117 }, 118 "inLanguage": ["en-US", "zh-CN", "zh-Hant"] 119 } 120 </script>
120 121 122
122<script type="application/ld+json"> 123 { 124 "@context": "https://schema.org", 125 "@type": "Organization", 126 "name": "ImgMin Pro", 127 "url": "https://imgmin.pro/", 128 "logo": "https://imgmin.pro/android-chrome-512x512.png", 129 "description": "Privacy-first online image compression tool. All processing happens client-side â images never leave the browser.", 130 "sameAs": [ 131 "https://x.com/ai_pmer", 132 "https://chromewebstore.google.com/detail/imgmin-%E2%80%93-image-compressor/helcbldoipinhoncicmimlekeocdmanl" 133 ] 134 } 135 </script>
135 136 137 <!-- FAQ Structured Data for Rich Snippets --> 138
138<script type="application/ld+json"> 139 { 140 "@context": "https://schema.org", 141 "@type": "FAQPage", 142 "mainEntity": [ 143 { 144 "@type": "Question", 145 "name": "How does ImgMin Pro reduce image size while preserving visual quality?", 146 "acceptedAnswer": { 147 "@type": "Answer", 148 "text": "ImgMin Pro uses the browser's native Canvas API to intelligently compress images. It analyzes each image and applies optimal compression settings based on your chosen mode (Balanced, Best Quality, or Smallest Size). All processing happens directly in your browser â no server upload required." 149 } 150 }, 151 { 152 "@type": "Question", 153 "name": "Is it safe to compress images with ImgMin Pro?", 154 "acceptedAnswer": { 155 "@type": "Answer", 156 "text": "Yes, ImgMin Pro is completely safe. Unlike most online compression tools, ImgMin Pro processes images entirely on your device using client-side JavaScript. Your images never leave your browser and are never uploaded to any server." 157 } 158 }, 159 { 160 "@type": "Question", 161 "name": "What image formats does ImgMin Pro support?", 162 "acceptedAnswer": { 163 "@type": "Answer", 164 "text": "ImgMin Pro supports JPG/JPEG, PNG, and WebP formats, and converts iPhone HEIC photos locally. You can compress up to 20 images at once using the batch upload feature, and download them individually or as a ZIP file." 165 } 166 }, 167 { 168 "@type": "Question", 169 "name": "Can I use ImgMin Pro offline?", 170 "acceptedAnswer": { 171 "@type": "Answer", 172 "text": "Yes, ImgMin Pro is a Progressive Web App (PWA) that works offline. Since all image compression happens in your browser without server communication, you can compress images even without an internet connection after your first visit." 173 } 174 } 175 ] 176 } 177 </script>
177 178 179 <link rel="stylesheet" href="styles.css?v=20260929a"> 180 <link rel="stylesheet" href="/site-header.css?v=20260924c"> 181 <!-- JSZip for multi-file download --> 182
182<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js" integrity="sha384-+mbV2IY1Zk/X1p/nWllGySJSUN8uMs+gUAN10Or95UBH0fpj6GfKgPmgC5EXieXG" crossorigin="anonymous"></script>
182 183</head> 184 185<body> 186 <noscript> 187 <div style="text-align:center;padding:3rem;font-family:sans-serif;"> 188 <h1>ImgMin Pro â Free Image Compressor</h1> 189 <p>ImgMin Pro is a free, privacy-first online image compression tool. Compress JPG, PNG, and WebP images directly in your browser â no upload required. All processing happens client-side for maximum privacy and security.</p> 190 <p>Please enable JavaScript to use ImgMin Pro.</p> 191 </div> 192 </noscript> 193 <nav class="navbar"> 194 <a href="/" class="logo" aria-label="ImgMin"> 195 <img src="/logo-nav.png" alt="ImgMin" width="514" height="120" class="logo-img-file"> 196 </a> 197 <div class="nav-right"> 198 <!-- Entry hidden 2026-09-16: bg-removal UX/effect not yet good enough for a 199 default-on nav link. Page and all logic stay live at /remove-background/ 200 and /zh/remove-background/; remove `hidden` here to relaunch. --> 201 <a href="/remove-background/" id="navRemoveBg" hidden style="color:#555;text-decoration:none;font-size:.9rem;font-weight:500;margin-right:.75rem">Remove BG</a> 202 <a href="/blog/" data-i18n="footer.blog" style="color:#555;text-decoration:none;font-size:.9rem;font-weight:500;margin-right:.75rem">Blog</a> 203 <select id="langSelector" class="lang-select"> 204 <option value="en-US">English</option> 205 <option value="zh-CN">ç®ä½ä¸æ</option> 206 <option value="zh-Hant">ç¹é«ä¸æ</option> 207 </select> 208 </div> 209 </nav> 210 211 <div class="container"> 212 <header> 213 <h1 data-i18n="hero.title">Compress Images Online â No Upload Required</h1> 214 <p data-i18n="hero.subtitle">Compress JPG, PNG & WebP locally while maintaining visual quality. Your images never leave your device.</p> 215 </header> 216 217 <a id="extBanner" class="ext-banner" href="https://chromewebstore.google.com/detail/imgmin-%E2%80%93-image-compressor/helcbldoipinhoncicmimlekeocdmanl?utm_source=imgmin.pro&utm_medium=banner&utm_campaign=ext_launch&utm_content=home" target="_blank" rel="noopener" onclick="if(window.gtag)gtag('event','ext_install_click',{location:'home_banner'})"> 218 <span class="ext-banner-tag" data-i18n="ext.banner.tag">NEW</span> 219 <span class="ext-banner-text" data-i18n="ext.banner.text">ImgMin for Chrome â right-click any image to compress.</span> 220 <span class="ext-banner-cta" data-i18n="ext.banner.cta">Add to Chrome â</span> 221 </a> 222 223 <main> 224 <!-- IMGMIN_TOOL:BEGIN --> 225<div class="upload-area" id="dropZone" role="button" tabindex="0" aria-label="Upload images"> 226 <input type="file" id="fileInput" accept="image/*" hidden multiple> 227 <div class="upload-content"> 228 <svg class="upload-icon" viewBox="0 0 24 24" width="48" height="48" fill="none" stroke="currentColor" stroke-width="1.5"> 229 <path d="M4 14.899A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.242" stroke-linecap="round" stroke-linejoin="round"/> 230 <path d="M12 12v9" stroke-linecap="round" stroke-linejoin="round"/> 231 <path d="m16 16-4-4-4 4" stroke-linecap="round" stroke-linejoin="round"/> 232 </svg> 233 <p data-i18n="upload.text">Click, drag or paste images here</p> 234 <button class="upload-btn" data-i18n="upload.btn">Select Images</button> 235 <p class="upload-hint" data-i18n="batch.upload.limit" 236 style="font-size: 0.8rem; opacity: 0.6; margin-top: 0.5rem;">Maximum 20 images at a time</p> 237 </div> 238 </div> 239 240 <div id="resizeChip" class="resize-chip" style="display:none;"> 241 <span id="resizeChipText"></span> 242 <button id="resizeChipDismiss" class="resize-chip-dismiss" aria-label="Dismiss">×</button> 243 </div> 244 245 <section class="task-toolbar" aria-label="Processing controls"> 246 <div class="compression-mode quick-modes" role="group" aria-label="Compression strategy"> 247 <button class="mode-btn active" data-mode="balanced" data-i18n="mode.balanced">Recommended</button> 248 <button class="mode-btn" data-mode="quality" data-i18n="mode.quality">Clearer</button> 249 <button class="mode-btn" data-mode="size" data-i18n="mode.size">Smaller file</button> 250 </div> 251 </section> 252 253 <div id="fileList" class="file-list" style="display: none;"> 254 <!-- JS will populate file items here --> 255 </div> 256 257 <div id="batchActions" class="batch-actions" style="display: none;">
258 <span id="batchStatusText" class="batch-status" aria-live="polite"></span> 259 <button id="clearAllBtn" class="batch-clear-btn" type="button" data-i18n="batch.clear">Clear All</button> 260 <button id="downloadAllBtn" class="primary-btn" data-i18n="batch.download">Download All</button> 261 <button id="shareRuleBtn" class="batch-clear-btn" type="button" hidden data-i18n="share.rule">Share settings</button> 262 </div> 263 264 <!-- Comparison Modal (triggered by eye icon) --> 265 <div id="comparisonModal" class="comparison-modal" role="dialog" aria-modal="true" aria-label="Image comparison" style="display: none;"> 266 <div class="comparison-modal-content"> 267 <button class="modal-close-btn" id="closeComparisonModal">×</button> 268 269 <!-- Mobile-only compact summary bar (hidden on desktop) --> 270 <div class="mobile-modal-summary" id="mobileModalSummary" style="display:none;"> 271 <div class="mobile-summary-row"> 272 <span class="mobile-summary-label" data-i18n="file.size">Size: </span> 273 <span id="mobileOrigSize" class="mobile-orig-size"></span> 274 <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 8h10M9 4l4 4-4 4"/></svg> 275 <span id="mobileCompSize" class="mobile-comp-size"></span> 276 <span id="mobileSavePct" class="mobile-save-pct"></span> 277 </div> 278 <div class="mobile-summary-resolution"> 279 <span data-i18n="file.resolution">Resolution: </span> 280 <strong id="mobileResolution"></strong> 281 </div> 282 </div> 283 284 <div class="mobile-preview-tabs" role="tablist" aria-label="Image comparison"> 285 <button type="button" id="mobileOriginalTab" class="mobile-preview-tab" role="tab" aria-selected="false" data-i18n="preview.original">Original</button> 286 <button type="button" id="mobileCompressedTab" class="mobile-preview-tab active" role="tab" aria-selected="true" data-i18n="preview.compressed">Compressed</button> 287 </div> 288 289 <div class="preview-container"> 290 <div class="preview-box preview-box-original"> 291 <h2 data-i18n="preview.original">Original</h2> 292 <div class="image-wrapper" id="originalWrapper"> 293 <img id="originalImage" alt="Original"> 294 </div> 295 <div class="file-info"> 296 <div class="file-info-row"> 297 <span data-i18n="file.size">Size: </span> 298 <span id="originalSize">0 KB</span> 299 </div> 300 <div class="file-info-row"> 301 <span data-i18n="file.resolution">Resolution: </span> 302 <span id="originalResolution">0 Ã 0</span> 303 </div> 304 </div> 305 </div> 306 307 <div class="preview-box preview-box-compressed"> 308 <h2 data-i18n="preview.compressed">Compressed</h2> 309 <div class="image-wrapper" id="compressedWrapper"> 310 <img id="compressedImage" alt="Compressed"> 311 </div> 312 <div class="crop-controls" id="cropControls" hidden> 313 <p data-i18n="crop.instruction">Drag the image to adjust the crop</p> 314 <div class="crop-actions"> 315 <button type="button" class="crop-cancel-btn" id="cropCancel" data-i18n="crop.cancel">Cancel</button> 316 <button type="button" class="crop-apply-btn" id="cropApply" data-i18n="crop.apply">Apply crop</button> 317 </div> 318 </div> 319 <div class="file-info"> 320 <div class="file-info-row">
321 <span data-i18n="file.size">Size: </span> 322 <span id="compressedSize">0 KB</span> 323 </div> 324 <div class="file-info-row"> 325 <span data-i18n="file.resolution">Resolution: </span> 326 <span id="compressedResolution">0 Ã 0</span> 327 </div> 328 </div> 329 </div> 330 </div> 331 332 <div id="statsReport" class="stats-report" style="display: none;"> 333 <div class="stats-icon">ð</div> 334 <div class="stats-content"> 335 <div class="stats-title" data-i18n="stats.title">Compression Analysis</div> 336 <div class="stats-text" id="statsText"></div> 337 </div> 338 </div> 339 340 <!-- Mobile-only download button inside modal --> 341 <button class="modal-mobile-download-btn" id="modalMobileDownloadBtn" data-i18n="download.single" style="display:none;">Download Image</button> 342 </div> 343 </div> 344 345 <div class="controls"> 346 <div class="options-wrapper"> 347 <details class="advanced-settings"> 348 <summary class="advanced-summary"> 349 <span id="advancedSummaryText" data-i18n="settings.advanced">Advanced Settings</span> 350 <svg class="arrow-icon" viewBox="0 0 24 24" width="20" height="20"> 351 <path d="M7 10l5 5 5-5z" fill="currentColor" /> 352 </svg> 353 </summary> 354 <div class="advanced-content"> 355 <section class="output-controls" aria-label="Output settings"> 356 <div class="target-shortcuts-head"> 357 <strong data-i18n="output.targetHeading">Target size</strong> 358 <span data-i18n="output.targetHint">Choose a common limit or enter a custom value</span> 359 </div> 360 <div class="target-shortcuts" role="group" aria-label="Target size shortcuts"> 361 <button type="button" class="target-shortcut" data-target-kb="50"><strong>50 KB</strong><small data-i18n="output.target50Hint">Avatars / IDs</small></button> 362 <button type="button" class="target-shortcut" data-target-kb="100"><strong>100 KB</strong><small data-i18n="output.target100Hint">Web / products</small></button> 363 <button type="button" class="target-shortcut" data-target-kb="200"><strong>200 KB</strong><small data-i18n="output.target200Hint">Clearer images</small></button> 364 </div> 365 <div class="output-field"> 366 <label for="targetKB" data-i18n="output.targetCustom">Custom limit (KB, optional)</label> 367 <input id="targetKB" type="number" min="1" max="100000" step="1" inputmode="numeric" aria-describedby="outputHint"> 368 </div> 369 <div class="output-field"> 370 <label for="outputFormat" data-i18n="output.format">Output format</label> 371 <select id="outputFormat"> 372 <option value="auto" data-i18n="output.auto">Keep original format</option> 373 <option value="jpeg">JPEG</option><option value="png">PNG</option><option value="webp">WebP</option> 374 </select> 375 </div> 376 <div class="output-field" id="jpegBackgroundControl" hidden>
377 <label for="jpegBackground" data-i18n="output.background">JPEG transparency background</label> 378 <input id="jpegBackground" type="color" value="#ffffff"> 379 </div> 380 <p id="outputHint" class="output-hint" data-i18n="output.hint">Leave blank for normal compression. 1 KB = 1000 bytes. With a target, JPEG/WebP quality follows the selected strategy. PNG keeps transparency and may exceed the target.</p> 381 <div class="local-rule-actions"> 382 <button id="saveRuleBtn" type="button" data-i18n="rule.save">Save these settings</button> 383 <button id="reuseRuleBtn" type="button" hidden data-i18n="rule.reuse">Reuse saved settings</button> 384 </div> 385 </section> 386 387 <div class="advanced-options"> 388 </div> 389 390 <div class="resize-control"> 391 <div class="resize-header"> 392 <h4 data-i18n="resize.title">Resize</h4> 393 <label class="toggle-switch"> 394 <input type="checkbox" id="resizeEnable" checked> 395 <span class="toggle-slider"></span> 396 </label> 397 </div> 398 <div class="resize-options" id="resizeOptions" style="display:flex;"> 399 <div class="resize-preset-row"> 400 <label data-i18n="resize.preset.label">Platform preset</label> 401 <select id="resizePreset"></select> 402 </div> 403 <div class="resize-dims-row"> 404 <div class="resize-dim-input"> 405 <label for="resizeWidth" data-i18n="resize.width">Width (px)</label> 406 <input type="number" id="resizeWidth" min="1" max="10000" placeholder="px"> 407 </div> 408 <span class="resize-dims-sep">Ã</span> 409 <div class="resize-dim-input"> 410 <label for="resizeHeight" data-i18n="resize.height">Height (px)</label> 411 <input type="number" id="resizeHeight" min="1" max="10000" placeholder="px"> 412 </div> 413 </div> 414 </div> 415 </div> 416 417 <!-- Rename Section --> 418 <div class="rename-control"> 419 <div class="rename-header"> 420 <span class="resize-title" data-i18n="rename.title">Batch Rename</span> 421 <label class="toggle-switch"> 422 <input type="checkbox" id="renameEnable"> 423 <span class="toggle-slider"></span> 424 </label> 425 </div> 426 <div class="rename-options" id="renameOptions" style="display:none;"> 427 <div class="rename-row"> 428 <span class="rename-pair"> 429 <label class="rename-label" data-i18n="rename.prefix">Prefix</label> 430 <input type="text" id="renamePrefix" class="rename-input" placeholder="" maxlength="40"> 431 </span> 432 <span class="rename-pair"> 433 <label class="rename-label" data-i18n="rename.suffix">Suffix</label> 434 <input type="text" id="renameSuffix" class="rename-input" placeholder="" maxlength="40"> 435 </span> 436 </div> 437 <div class="rename-row rename-check-row"> 438 <label class="rename-check-label"> 439 <input type="checkbox" id="renameKeepOriginal" checked>
440 <span data-i18n="rename.keep_original">Keep original name</span> 441 </label> 442 <label class="rename-check-label"> 443 <input type="checkbox" id="renameSeq"> 444 <span data-i18n="rename.sequence">Add number</span> 445 </label> 446 <span id="renameSeqOptions" style="display:none; align-items:center; gap:.4rem; flex-wrap:wrap;"> 447 <label class="rename-label" data-i18n="rename.seq_start">Start</label> 448 <input type="number" id="renameSeqStart" class="rename-input rename-input-sm" value="1" min="0" max="9999"> 449 <label class="rename-label" data-i18n="rename.seq_padding">Digits</label> 450 <input type="number" id="renameSeqPad" class="rename-input rename-input-sm" value="2" min="1" max="4"> 451 </span> 452 </div> 453 <div class="rename-preview" id="renamePreview" style="display:none;"> 454 <span class="rename-preview-label" data-i18n="rename.preview_label">Preview:</span> 455 <span id="renamePreviewText" class="rename-preview-text"></span> 456 </div> 457 </div> 458 </div> 459 460 <!-- Watermark Section (hidden until ready to launch) --> 461 <div class="watermark-control" style="display:none;"> 462 <div class="rename-header"> 463 <label class="toggle-switch"> 464 <input type="checkbox" id="watermarkEnable"> 465 <span class="toggle-slider"></span> 466 </label> 467 <span class="resize-title" data-i18n="watermark.title">Watermark</span> 468 </div> 469 <div class="watermark-options" id="watermarkOptions" style="display:none;"> 470 <div class="wm-row"> 471 <label class="rename-label" data-i18n="watermark.text.label">Text</label> 472 <input type="text" id="watermarkText" class="rename-input wm-text-input" data-i18n-placeholder="watermark.text.placeholder" placeholder="© All rights reserved" maxlength="60"> 473 </div> 474 <div class="wm-row wm-logo-row"> 475 <label class="rename-label" data-i18n="watermark.image.label">Logo</label> 476 <label class="wm-upload-btn" for="watermarkImageInput"> 477 <svg viewBox="0 0 20 20" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><path d="M10 3v10M5 8l5-5 5 5"/><path d="M3 15h14"/></svg> 478 <span id="watermarkImageLabel" data-i18n="watermark.image.upload">Upload logo</span> 479 </label> 480 <input type="file" id="watermarkImageInput" accept="image/*" style="display:none;"> 481 <button id="watermarkImageClear" class="wm-clear-btn" style="display:none;" title="Remove logo">Ã</button> 482 </div> 483 <div class="wm-row"> 484 <label class="rename-label" data-i18n="watermark.opacity">Opacity</label> 485 <input type="range" id="watermarkOpacity" min="10" max="100" value="50" class="wm-opacity-slider">
486 <span id="watermarkOpacityVal" class="rename-label">50%</span> 487 </div> 488 <div class="wm-row wm-pos-row"> 489 <label class="rename-label" data-i18n="watermark.position">Position</label> 490 <div class="wm-grid" id="watermarkGrid"> 491 <button data-pos="top-left" class="wm-cell"></button> 492 <button data-pos="top-center" class="wm-cell"></button> 493 <button data-pos="top-right" class="wm-cell"></button> 494 <button data-pos="center-left" class="wm-cell"></button> 495 <button data-pos="center" class="wm-cell"></button> 496 <button data-pos="center-right" class="wm-cell"></button> 497 <button data-pos="bottom-left" class="wm-cell"></button> 498 <button data-pos="bottom-center" class="wm-cell"></button> 499 <button data-pos="bottom-right" class="wm-cell active"></button> 500 </div> 501 </div> 502 </div> 503 </div> 504 505 </div> 506 </details> 507 </div> 508 </div> 509 510 511 <div class="modal" id="imageModal"> 512 <div class="modal-content"> 513 <button class="modal-close" aria-label="Close">Ã</button> 514 <img id="modalImage" alt="Preview"> 515 </div> 516 </div> 517 <!-- IMGMIN_TOOL:END --> 518 <section id="globalStats" class="global-stats"> 519 <h2 class="stats-section-title" data-i18n="stats.session">Your local totals</h2> 520 <div class="stats-grid"> 521 <div class="stat-item"> 522 <div class="stat-icon"> 523 <svg viewBox="0 0 24 24" width="28" height="28" fill="none" stroke="currentColor" 524 stroke-width="2"> 525 <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect> 526 <circle cx="8.5" cy="8.5" r="1.5"></circle> 527 <polyline points="21 15 16 10 5 21"></polyline> 528 </svg> 529 </div> 530 <div class="stat-info"> 531 <div class="stat-value"><span id="statImages">0</span><span class="stat-unit" data-i18n="unit.images">images</span></div> 532 <div class="stat-label" data-i18n="stats.images">Images Processed</div> 533 </div> 534 </div> 535 <div class="stat-item"> 536 <div class="stat-icon"> 537 <svg viewBox="0 0 24 24" width="28" height="28" fill="none" stroke="currentColor" 538 stroke-width="2"> 539 <path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"></path> 540 <polyline points="17 21 17 13 7 13 7 21"></polyline> 541 <polyline points="7 3 7 8 15 8"></polyline> 542 </svg> 543 </div> 544 <div class="stat-info"> 545 <div class="stat-value"><span id="statSaved">0</span><span class="stat-unit">M</span></div> 546 <div class="stat-label" data-i18n="stats.saved">Space Saved</div> 547 </div> 548 </div> 549 </div> 550 <p class="stats-estimate" data-i18n="stats.estimate">Only successful tasks in this browser are counted. Clearing browser storage resets these totals.</p> 551 </section> 552 553 <!-- BUILD1: Privacy Verification Bar â below globalStats --> 554 <div class="privacy-verify-bar" id="privacyVerifyBar"> 555 <div class="pvb-left">
556 <span class="pvb-dot"></span> 557 <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5" style="flex-shrink:0;opacity:0.7"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><path d="M9 12l2 2 4-4"/></svg> 558 <span class="pvb-text" data-i18n-html="pvb.text">Your images <strong>never leave your browser</strong> â zero upload, verified.</span> 559 </div> 560 <button class="pvb-toggle" id="pvbToggle" aria-expanded="false" aria-controls="pvbGuide" data-i18n="pvb.toggle">How to verify âº</button> 561 </div> 562 <div class="pvb-guide" id="pvbGuide" hidden> 563 <p><span data-i18n-html="pvb.guide">Open <strong>Chrome DevTools</strong> (F12) â Network tab â filter Img â compress a photo. You will see 0 image upload requests.</span> <a href="/privacy-test/" data-i18n="pvb.guide_link">Try offline demo â</a></p> 564 </div> 565 <!-- END BUILD1 --> 566 567 </main> 568 569 <footer class="footer"> 570 <div class="footer-contact"> 571 <a href="https://x.com/ai_pmer" target="_blank" rel="noopener noreferrer" class="social-link" 572 data-i18n-title="footer.follow_us"> 573 <svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"> 574 <path 575 d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"> 576 </path> 577 </svg> 578 </a> 579 </div> 580 581 <div class="footer-links"> 582 <a href="/blog/" data-i18n="footer.blog">Blog</a> 583 <a href="/compress-jpg/" data-i18n="footer.compress_jpg">Compress JPG</a> 584 <a href="/compress-png/" data-i18n="footer.compress_png">Compress PNG</a> 585 <a href="/compress-webp/" data-i18n="footer.compress_webp">Compress WebP</a> 586 <a href="/compress-without-uploading/" data-i18n="footer.no_upload">No-Upload Compressor</a> 587 <a href="./privacy.html" data-i18n="footer.privacy">Privacy</a> 588 <a href="https://pmer.cn" target="_blank" rel="nofollow noopener">More tools by the creator</a> 589 </div> 590 591 <div class="trust-badges"> 592 <div class="trust-badge"> 593 <svg viewBox="0 0 24 24" class="trust-icon" fill="none" stroke="currentColor" stroke-width="2"> 594 <path d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> 595 <path d="M9 12l2 2 4-4" /> 596 </svg> 597 <span data-i18n="trust.local">Local Processing</span> 598 </div> 599 <div class="trust-badge"> 600 <svg viewBox="0 0 24 24" class="trust-icon" fill="none" stroke="currentColor" stroke-width="2"> 601 <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" /> 602 <path d="M9 12l2 2 4-4" /> 603 </svg> 604 <span data-i18n="trust.no_upload">Zero Data Upload</span> 605 </div> 606 607 </div> 608 609 <div class="footer-info"> 610 <p data-i18n="footer.copyright">© 2026 ImgMin Pro. All rights reserved.</p> 611 </div> 612 <div class="gdpr-notice"> 613 <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" fill="none" stroke-width="2"> 614 <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path> 615 </svg> 616 <span data-i18n="gdpr.text">Client-side processing. No server upload.</span> 617 </div> 618 </footer> 619 </div> 620 621 <!-- BUILD1: pvb toggle â uses i18n for button label, runs after i18n.js --> 622
622<script> 623 (function(){ 624 function initPvb() { 625 var btn = document.getElementById('pvbToggle'); 626 var guide = document.getElementById('pvbGuide'); 627 if (!btn || !guide) return; 628 btn.addEventListener('click', function(){ 629 var open = btn.getAttribute('aria-expanded') === 'true'; 630 btn.setAttribute('aria-expanded', String(!open)); 631 guide.hidden = open; 632 // Use i18n if available, fallback to English 633 var i18n = window.imgminI18n; 634 btn.textContent = open 635 ? (i18n ? i18n.t('pvb.toggle') : 'How to verify âº') 636 : (i18n ? i18n.t('pvb.toggle_close') : 'Hide â'); 637 }); 638 } 639 // Run after DOM + i18n ready 640 if (document.readyState === 'loading') { 641 document.addEventListener('DOMContentLoaded', initPvb); 642 } else { 643 initPvb(); 644 } 645 })(); 646 </script>
646 647 <!-- END BUILD1 --> 648
648<script>window.IMGMIN_I18N_LOCK = true;</script>
648 649
649<script src="/i18n-zh-hant.js?v=20260924b"></script>
649 650
650<script src="i18n.js?v=20260924c"></script>
650 651
651<script> 652 // Placed after i18n.js deliberately: document.documentElement.lang is 653 // only correct once I18n.init() has run â on the root "/" page the 654 // language is detected at runtime (cached preference or browser 655 // language), so checking this any earlier would always see the 656 // page's static "en" attribute regardless of what actually renders. 657 // This is baked into all six generated language homepages as-is 658 // (build-i18n.js only rewrites text inside data-i18n attributes, 659 // never
659<script> content); it only takes effect where the visitor's 660 // language resolves to Chinese, since /zh/remove-background/ is the 661 // only translated variant so far. 662 (function () { 663 var navRemoveBg = document.getElementById('navRemoveBg'); 664 if (!navRemoveBg) return; 665 function syncNavRemoveBg() { 666 var isZh = (document.documentElement.lang || '').toLowerCase().indexOf('zh') === 0; 667 navRemoveBg.href = isZh ? '/zh/remove-background/' : '/remove-background/'; 668 navRemoveBg.textContent = isZh ? 'å»èæ¯' : 'Remove BG'; 669 } 670 syncNavRemoveBg(); 671 // The langSelector dropdown switches language client-side, without 672 // a navigation, so this has to re-run on that event too â not just 673 // once at load. 674 window.addEventListener('imgmin:languagechange', syncNavRemoveBg); 675 })(); 676 </script>
676 677
677<script src="target-encoder.js?v=20260910a"></script>
677 678
678<script src="analytics.js?v=20260924b"></script>
678 679
679<script src="script.js?v=20260929a"></script>
679 680
680<script src="snow.js"></script>
680 681
681<script> 682 if ('serviceWorker' in navigator) { 683 window.addEventListener('load', () => { 684 navigator.serviceWorker.register('/sw.js').catch(() => { 685 // Offline caching is progressive enhancement; image processing still works online. 686 }); 687 }); 688 } 689 </script>
689 690 <button id="floatingDownload" data-i18n="batch.download" aria-label="Download all compressed images" aria-hidden="true" tabindex="-1">Download All</button> 691 692 <!-- Cloudflare Web Analytics -->
vendor: 99 bytes, line 692
692<script defer src='https://static.cloudflareinsights.com/beacon.min.js' data-cf-beacon='{"token": "
69220ba486752104a028f3a399fe7cf82c7
vendor: 13 bytes, line 692
692"}'></script>
692<!-- End Cloudflare Web Analytics --> 693</body> 694 695</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.