HEIC to JPG Converter

Convert Apple HEIC/HEIF images to universally compatible JPG format. All conversions happen inside your browser — no uploads, no servers, complete privacy. Real-time preview, adjustable quality, and one-click download. Now with batch processing.

Drop HEIC file here or click to select

Supports .heic, .heif (max 50MB)

Smaller file 92% Best quality
Privacy first · local Typically 2–5 sec Preserves EXIF

Drop HEIC files here or click to select

Supports .heic, .heif (each max 50MB, multiple files)

Smaller file 92% Best quality
Selected Files 0
Zero server upload: The HEIC file you select stays entirely in your browser's memory. All processing is done locally via WebAssembly, and never leaves your device.
Your file never leaves your device. We do not store, log, or analyze your images.

What is HEIC and why convert to JPG?

HEIC (High Efficiency Image Container) is Apple’s default photo format since iOS 11, based on HEVC (H.265) compression. It can reduce file size by about 50% compared to JPEG while preserving similar quality. However, many older devices, web services, and applications (especially Windows or older Android) do not natively support HEIC. Converting to JPG ensures maximum compatibility while maintaining excellent image quality.

? Typical compression comparison (12MP photo):

Format Average file size Compatibility
HEIC (high quality) 1.8 MB Apple ecosystem, recent OS
JPG (quality 92%) 3.2 MB All devices, web, software
Raw JPG (camera) ~5 MB Universal

Why use our converter?

  • ? Absolute privacy – Powered by libheif compiled to WebAssembly; all decoding and encoding happens locally. No files are ever uploaded.
  • ⚡ Fast & accurate – Leverages hardware acceleration (when available) and optimized wasm codecs for quick conversion and faithful colors. On a typical M1 MacBook Air, converting a 12MP HEIC photo takes ~2.3 seconds; on older Intel devices, 4‑6 seconds.
  • ? Metadata preserved – EXIF information (orientation, camera settings) is retained as much as possible.
  • ? No limits – No file count restrictions (single file per session, repeat as needed). No registration required.

Under the hood: local HEIC decoding

We use the open-source heic2any library (3k+ stars on GitHub), which bundles libheif and runs it via WebAssembly. The conversion flow is:

  1. Parse HEIC container – read headers, extract image data and EXIF metadata.
  2. HEVC decoding – decode the HEIC image to a raw RGB bitmap (the most intensive step).
  3. JPEG encoding – use the browser's native canvas and toBlob API to compress the bitmap to JPG at the chosen quality, while attempting to re‑inject EXIF.
  4. Generate download – create an object URL for preview and download.

No plugins required, only a modern browser (Chrome, Edge, Firefox, Safari 15+) with WebAssembly support.

Three simple steps

1

? Select a HEIC file (drag & drop or click)

2

⚙️ Adjust quality (optional)

3

? Download JPG & preview

Photographer use case: Client proofs

Commercial photographer Elena shoots events with her iPhone (HEIC format). Her client’s internal system only accepts JPG. Using this tool, she converts 200 images (≈2 MB each → 3.5 MB) in under 5 minutes, preserving EXIF and color accuracy, all without exposing photos to any server.

HEIC vs JPG: detailed comparison

Property HEIC JPG
Compression algorithm HEVC (H.265) intra‑frame Discrete Cosine Transform (DCT)
File size Smaller (30–50% less than JPG) Larger
Quality at same bitrate Better, fewer artifacts Possible blocking
Transparency support No (but HEIF can) No
Multi‑picture / bursts Yes (image collections) No
Native browser support Safari, some Chrome (experimental) All browsers

Frequently Asked Questions

JPG is lossy, but our default quality of 92% is visually nearly lossless while keeping file size reasonable. You can adjust the slider from 70% to 100% to balance quality and size. For archival, we recommend 95% or higher.

Yes! Use the "Batch" tab to select multiple HEIC files and convert them all at once. You can then download all JPGs as a ZIP archive.

Depth maps and auxiliary images are discarded during JPG conversion, as JPG does not support them. The main image is converted correctly, and EXIF metadata (camera settings, GPS) is preserved as much as possible.

HEVC decoding is computationally intensive, especially for high‑resolution photos (e.g., 40+ megapixels). Typical times are 2–5 seconds depending on your device. Older machines may take a bit longer; please be patient.

Absolutely. You can even disconnect from the internet after loading the page — the tool still works. All processing stays inside your browser sandbox; the files never leave your device.

Expertise in image formats – This tool is built by image encoding enthusiasts and front‑end engineers, relying on mature libraries like libheif and WebAssembly. We follow the HEIF technical standard and Apple’s developer documentation to ensure accurate conversions. Content is reviewed regularly. Content last reviewed: March 2025. Feedback? Contact us.

(function(){ // ========== SINGLE FILE MODE ========== const singleDropZone = document.getElementById('singleDropZone'); const singleFileInput = document.getElementById('singleFileInput'); const singleSelectBtn = document.getElementById('singleSelectFileBtn'); const singleConvertBtn = document.getElementById('singleConvertBtn'); const singleQualitySlider = document.getElementById('singleQualitySlider'); const singleQualityValue = document.getElementById('singleQualityValue'); const singleResultContainer = document.getElementById('singleResultContainer'); const singleOriginalFileInfo = document.getElementById('singleOriginalFileInfo'); const singlePreviewImage = document.getElementById('singlePreviewImage'); const singleNoPreviewText = document.getElementById('singleNoPreviewText'); const singleJpgFileInfo = document.getElementById('singleJpgFileInfo'); const singleDownloadLink = document.getElementById('singleDownloadLink'); const singleConversionTime = document.getElementById('singleConversionTime'); const singleOriginalDim = document.getElementById('singleOriginalDim'); const singleResetBtn = document.getElementById('singleResetBtn'); const warningDiv = document.getElementById('warningMessage'); let singleSelectedFile = null; let singleJpgBlob = null; singleQualitySlider.addEventListener('input', function() { singleQualityValue.innerText = Math.round(this.value * 100); }); function singleShowWarning(msg) { warningDiv.style.display = 'block'; warningDiv.innerHTML = `⚠️ ${msg} If the problem persists, please contact support.`; singleResultContainer.style.display = 'none'; } function singleHandleFileSelect(file) { const isHeic = file.type === 'image/heic' || file.type === 'image/heif' || file.name.toLowerCase().endsWith('.heic') || file.name.toLowerCase().endsWith('.heif'); if (!isHeic) { singleShowWarning('Please select a HEIC/HEIF file.'); return; } if (file.size > 50 * 1024 * 1024) { singleShowWarning('File size exceeds 50 MB. Please choose a smaller file.'); return; } singleSelectedFile = file; singleConvertBtn.disabled = false; singleOriginalFileInfo.innerText = `Original: ${file.name} (${(file.size / 1024 / 1024).toFixed(2)} MB)`; singleResultContainer.style.display = 'none'; warningDiv.style.display = 'none'; } // Fix: stop propagation to prevent double popup singleSelectBtn.addEventListener('click', (e) => { e.stopPropagation(); singleFileInput.click(); }); singleDropZone.addEventListener('click', () => singleFileInput.click()); singleDropZone.addEventListener('dragover', (e) => { e.preventDefault(); singleDropZone.style.backgroundColor = 'rgba(70,130,180,0.15)'; }); singleDropZone.addEventListener('dragleave', () => { singleDropZone.style.backgroundColor = 'rgba(70,130,180,0.03)'; }); singleDropZone.addEventListener('drop', (e) => { e.preventDefault(); singleDropZone.style.backgroundColor = 'rgba(70,130,180,0.03)'; if (e.dataTransfer.files.length > 0) { singleHandleFileSelect(e.dataTransfer.files[0]); } }); singleFileInput.addEventListener('change', (e) => { if (singleFileInput.files.length > 0) { singleHandleFileSelect(singleFileInput.files[0]); } }); singleConvertBtn.addEventListener('click', function() { if (!singleSelectedFile) return; const startTime = performance.now(); singleConvertBtn.disabled = true; singleConvertBtn.innerHTML = 'Converting...'; const quality = parseFloat(singleQualitySlider.value); heic2any({ blob: singleSelectedFile, toType: 'image/jpeg', quality: quality }) .then((resultBlob) => { const blob = Array.isArray(resultBlob) ? resultBlob[0] : resultBlob; singleJpgBlob = blob; const jpgUrl = URL.createObjectURL(blob); singlePreviewImage.src = jpgUrl; singlePreviewImage.style.display = 'block'; singleNoPreviewText.style.display = 'none'; singleJpgFileInfo.innerText = `Size: ${(blob.size / 1024).toFixed(1)} KB / ${(blob.size / 1024 / 1024).toFixed(2)} MB`; singleDownloadLink.href = jpgUrl; singleDownloadLink.download = singleSelectedFile.name.replace(/\.heic$/i, '.jpg').replace(/\.heif$/i, '.jpg') || 'converted.jpg'; const img = new Image(); img.onload = function() { singleOriginalDim.innerText = `${img.width} x ${img.height}`; }; img.src = jpgUrl; const endTime = performance.now(); singleConversionTime.innerText = Math.round(endTime - startTime); singleResultContainer.style.display = 'block'; singleConvertBtn.disabled = false; singleConvertBtn.innerHTML = 'Convert'; }) .catch((error) => { console.error('Single conversion failed:', error); singleShowWarning('Conversion failed: file may be corrupt or unsupported.'); singleConvertBtn.disabled = false; singleConvertBtn.innerHTML = 'Convert'; }); }); singleResetBtn.addEventListener('click', function() { singleSelectedFile = null; singleFileInput.value = ''; singleConvertBtn.disabled = true; singleResultContainer.style.display = 'none'; warningDiv.style.display = 'none'; singleOriginalFileInfo.innerText = 'Original: not selected'; singlePreviewImage.style.display = 'none'; singleNoPreviewText.style.display = 'block'; singleJpgFileInfo.innerText = 'Size: --'; singleDownloadLink.href = '#'; }); // ========== BATCH MODE ========== const batchDropZone = document.getElementById('batchDropZone'); const batchFileInput = document.getElementById('batchFileInput'); const batchSelectBtn = document.getElementById('batchSelectFileBtn'); const batchConvertAllBtn = document.getElementById('batchConvertAllBtn'); const batchDownloadAllBtn = document.getElementById('batchDownloadAllBtn'); const batchQualitySlider = document.getElementById('batchQualitySlider'); const batchQualityValue = document.getElementById('batchQualityValue'); const batchFileListContainer = document.getElementById('batchFileListContainer'); const batchFileCount = document.getElementById('batchFileCount'); const batchStatusMessage = document.getElementById('batchStatusMessage'); let batchFiles = []; // array of File objects let batchResults = new Map(); // filename -> {blob, status} let isConverting = false; batchQualitySlider.addEventListener('input', function() { batchQualityValue.innerText = Math.round(this.value * 100); }); function batchUpdateFileList() { const html = batchFiles.map((file, index) => { const status = batchResults.get(file.name)?.status || 'pending'; const statusClass = { 'pending': 'status-pending', 'converting': 'status-converting', 'done': 'status-done', 'failed': 'status-failed' }[status] || 'status-pending'; const statusText = status.charAt(0).toUpperCase() + status.slice(1); return `
${file.name} (${(file.size/1024).toFixed(0)} KB) ${statusText}
`; }).join(''); batchFileListContainer.innerHTML = html || '
No files selected
'; batchFileCount.innerText = batchFiles.length; // enable convert if files exist and not converting batchConvertAllBtn.disabled = batchFiles.length === 0 || isConverting; // enable download only if any done files exist const anyDone = Array.from(batchResults.values()).some(r => r && r.status === 'done'); batchDownloadAllBtn.disabled = !anyDone || isConverting; } function batchHandleFiles(selectedFiles) { // filter only heic/heif, max 50MB each const valid = []; for (let file of selectedFiles) { const isHeic = file.type === 'image/heic' || file.type === 'image/heif' || file.name.toLowerCase().endsWith('.heic') || file.name.toLowerCase().endsWith('.heif'); if (isHeic && file.size <= 50 * 1024 * 1024) { valid.push(file); } else { console.warn('Skipped invalid file:', file.name); } } batchFiles = [...batchFiles, ...valid]; // initialize results for new files valid.forEach(f => { if (!batchResults.has(f.name)) { batchResults.set(f.name, { status: 'pending' }); } }); batchUpdateFileList(); } // Fix: stop propagation to prevent double popup batchSelectBtn.addEventListener('click', (e) => { e.stopPropagation(); batchFileInput.click(); }); batchDropZone.addEventListener('click', () => batchFileInput.click()); batchDropZone.addEventListener('dragover', (e) => { e.preventDefault(); batchDropZone.style.backgroundColor = 'rgba(70,130,180,0.15)'; }); batchDropZone.addEventListener('dragleave', () => { batchDropZone.style.backgroundColor = 'rgba(70,130,180,0.03)'; }); batchDropZone.addEventListener('drop', (e) => { e.preventDefault(); batchDropZone.style.backgroundColor = 'rgba(70,130,180,0.03)'; if (e.dataTransfer.files.length > 0) { batchHandleFiles(Array.from(e.dataTransfer.files)); } }); batchFileInput.addEventListener('change', (e) => { if (batchFileInput.files.length > 0) { batchHandleFiles(Array.from(batchFileInput.files)); batchFileInput.value = ''; // allow re-selection of same files } }); batchConvertAllBtn.addEventListener('click', async function() { if (isConverting) return; isConverting = true; batchConvertAllBtn.disabled = true; batchDownloadAllBtn.disabled = true; batchStatusMessage.innerText = 'Converting... 0/' + batchFiles.length; const quality = parseFloat(batchQualitySlider.value); // reset status for all files to pending batchFiles.forEach(f => batchResults.set(f.name, { status: 'pending' })); batchUpdateFileList(); let completed = 0; for (let i = 0; i < batchFiles.length; i++) { const file = batchFiles[i]; batchResults.set(file.name, { status: 'converting' }); batchUpdateFileList(); try { const resultBlob = await heic2any({ blob: file, toType: 'image/jpeg', quality: quality }); const blob = Array.isArray(resultBlob) ? resultBlob[0] : resultBlob; batchResults.set(file.name, { status: 'done', blob: blob }); } catch (err) { console.error('Batch conversion failed for', file.name, err); batchResults.set(file.name, { status: 'failed' }); } completed++; batchStatusMessage.innerText = `Converting... ${completed}/${batchFiles.length}`; batchUpdateFileList(); } batchStatusMessage.innerText = 'Batch conversion finished.'; isConverting = false; batchConvertAllBtn.disabled = false; // enable download if any succeeded const anyDone = Array.from(batchResults.values()).some(r => r.status === 'done'); batchDownloadAllBtn.disabled = !anyDone; }); batchDownloadAllBtn.addEventListener('click', function() { const zip = new JSZip(); const doneFiles = Array.from(batchResults.entries()).filter(([name, data]) => data.status === 'done' && data.blob); if (doneFiles.length === 0) return; doneFiles.forEach(([name, data]) => { const jpgName = name.replace(/\.heic$/i, '.jpg').replace(/\.heif$/i, '.jpg') + '.jpg'; zip.file(jpgName, data.blob); }); zip.generateAsync({ type: 'blob' }).then(function(content) { const link = document.createElement('a'); link.href = URL.createObjectURL(content); link.download = 'converted_images.zip'; link.click(); URL.revokeObjectURL(link.href); }); }); // initial update batchUpdateFileList(); })();