Stop converting images one by one. This guide shows you how to convert hundreds of images in seconds using free tools, command-line scripts, and automated production pipelines.
⚡ Why Batch Conversion?
Without batch conversion:
100 images × 3 minutes each = 5 hours of manual work 😩
With batch conversion:
100 images × one command = 30 seconds ✅
🌐 Method 1 — Free Web Tools (Best for Beginners)
ClearAI Tools
Steps:
1. Go to clearaitools.com
2. Select "Batch Convert"
3. Drag and drop your files (up to 50 images)
4. Choose WebP or AVIF as output format
5. Download the ZIP file containing all converted images
Pros:
✅ No installation required
✅ Free to use
✅ Works entirely in your browser
✅ Supports JPEG, PNG, GIF → WebP, AVIF
Limitations:
⚠️ Files are uploaded to a server (privacy consideration)
⚠️ Slower than local tools
⚠️ File size limits apply
Free Web Tool Comparison
| Tool | Max Files | Formats | Speed | Free? |
|---|---|---|---|---|
| ClearAI Tools | 50 files | WebP, AVIF | Medium | ✅ |
| Convertio | 25 files | Many | Medium | ✅ Partial |
| Squoosh | 1 file | Many | Fast | ✅ |
| ILoveIMG | 30 files | Many | Medium | ✅ Partial |
| Kraken.io | 20 MB | WebP, JPEG, PNG | Fast | ✅ Partial |
💻 Method 2 — Command Line (For Power Users)
Setup
# macOS
brew install webp imagemagick
# Ubuntu / Debian
sudo apt-get install webp imagemagick
# Windows (PowerShell with Chocolatey)
choco install webp imagemagick
Core Batch Conversion Commands
# ─── JPEG → WebP ───
# Convert all JPEGs in the current folder
for f in *.jpg *.jpeg; do
cwebp -q 80 "$f" -o "${f%.*}.webp" && echo "✅ $f"
done
# ─── PNG → WebP (lossless) ───
for f in *.png; do
cwebp -lossless "$f" -o "${f%.*}.webp" && echo "✅ $f"
done
# ─── All images recursively (including subfolders) ───
find . \( -name "*.jpg" -o -name "*.png" \) | while read f; do
cwebp -q 80 "$f" -o "${f%.*}.webp"
done
# ─── Convert + resize (max width 1200px) ───
for f in *.jpg; do
convert "$f" -resize 1200x1200\> \
-quality 80 \
"${f%.jpg}.webp"
done
Advanced Script — With Statistics Report
#!/bin/bash
# batch-convert.sh — batch convert with savings report
INPUT_DIR="./images"
OUTPUT_DIR="./images-webp"
QUALITY=80
mkdir -p "$OUTPUT_DIR"
total_before=0
total_after=0
count=0
for f in "$INPUT_DIR"/*.{jpg,jpeg,png,gif}; do
[ -f "$f" ] || continue
filename=$(basename "$f")
name="${filename%.*}"
output="$OUTPUT_DIR/$name.webp"
before=$(wc -c < "$f")
cwebp -q $QUALITY "$f" -o "$output" -quiet
after=$(wc -c < "$output")
saving=$(echo "scale=1; (1 - $after / $before) * 100" | bc)
echo "✅ $filename → $name.webp | Before: ${before}B | After: ${after}B | Saved: ${saving}%"
total_before=$((total_before + before))
total_after=$((total_after + after))
count=$((count + 1))
done
total_saving=$(echo "scale=1; (1 - $total_after / $total_before) * 100" | bc)
echo ""
echo "═══════════════════════════════════════════"
echo "📊 Summary:"
echo " Images converted: $count"
echo " Size before: $((total_before / 1024)) KB"
echo " Size after: $((total_after / 1024)) KB"
echo " Total saved: ${total_saving}%"
echo "═══════════════════════════════════════════"
🟢 Method 3 — Node.js with Sharp (For Developers)
Sharp is the fastest Node.js image processing library and the go-to choice for production pipelines.
// batch-convert.js
const sharp = require('sharp');
const fs = require('fs');
const path = require('path');
const { promisify } = require('util');
const readdir = promisify(fs.readdir);
const CONFIG = {
inputDir: './images',
outputDir: './images-optimized',
quality: {
webp: 80,
avif: 65,
},
maxWidth: 1920,
};
async function convertImage(inputPath, outputDir) {
const filename = path.basename(inputPath, path.extname(inputPath));
const stats = fs.statSync(inputPath);
// WebP for modern browsers
const webpPath = path.join(outputDir, `${filename}.webp`);
await sharp(inputPath)
.resize(CONFIG.maxWidth, null, { withoutEnlargement: true })
.webp({ quality: CONFIG.quality.webp })
.toFile(webpPath);
// AVIF for cutting-edge browsers
const avifPath = path.join(outputDir, `${filename}.avif`);
await sharp(inputPath)
.resize(CONFIG.maxWidth, null, { withoutEnlargement: true })
.avif({ quality: CONFIG.quality.avif })
.toFile(avifPath);
const webpStats = fs.statSync(webpPath);
const saving = ((1 - webpStats.size / stats.size) * 100).toFixed(1);
return {
file: filename,
before: stats.size,
after: webpStats.size,
saving: `${saving}%`,
};
}
async function batchConvert() {
if (!fs.existsSync(CONFIG.outputDir)) {
fs.mkdirSync(CONFIG.outputDir, { recursive: true });
}
const files = await readdir(CONFIG.inputDir);
const imageFiles = files.filter(f => /\.(jpg|jpeg|png|gif)$/i.test(f));
console.log(`🚀 Converting ${imageFiles.length} images...\n`);
const results = [];
for (const file of imageFiles) {
const inputPath = path.join(CONFIG.inputDir, file);
try {
const result = await convertImage(inputPath, CONFIG.outputDir);
results.push(result);
console.log(`✅ ${result.file} | Saved: ${result.saving}`);
} catch (err) {
console.error(`❌ Error on ${file}: ${err.message}`);
}
}
const totalBefore = results.reduce((s, r) => s + r.before, 0);
const totalAfter = results.reduce((s, r) => s + r.after, 0);
const totalSaving = ((1 - totalAfter / totalBefore) * 100).toFixed(1);
console.log(`\n${'═'.repeat(50)}`);
console.log(`📊 Total images: ${results.length}`);
console.log(` Size before: ${(totalBefore / 1024).toFixed(0)} KB`);
console.log(` Size after: ${(totalAfter / 1024).toFixed(0)} KB`);
console.log(` Total saved: ${totalSaving}%`);
}
batchConvert();
Run:
npm install sharp
node batch-convert.js
🐍 Method 4 — Python with Pillow
# batch_convert.py
from PIL import Image
from pathlib import Path
def convert_to_webp(input_dir: str, output_dir: str, quality: int = 80):
input_path = Path(input_dir)
output_path = Path(output_dir)
output_path.mkdir(exist_ok=True)
extensions = {'.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff'}
images = [f for f in input_path.iterdir() if f.suffix.lower() in extensions]
print(f"🚀 Converting {len(images)} images...\n")
total_before = total_after = 0
for img_path in images:
output_file = output_path / (img_path.stem + '.webp')
before_size = img_path.stat().st_size
with Image.open(img_path) as img:
if img.mode in ('RGBA', 'LA', 'P'):
img = img.convert('RGBA')
img.save(output_file, 'WEBP', quality=quality, method=6)
after_size = output_file.stat().st_size
saving = (1 - after_size / before_size) * 100
print(f"✅ {img_path.name} → {output_file.name} | -{saving:.1f}%")
total_before += before_size
total_after += after_size
total_saving = (1 - total_after / total_before) * 100
print(f"\n{'═' * 50}")
print(f"📊 Size before: {total_before // 1024} KB")
print(f" Size after: {total_after // 1024} KB")
print(f" Saved: {total_saving:.1f}%")
if __name__ == '__main__':
convert_to_webp('./images', './images-webp', quality=80)
pip install Pillow
python batch_convert.py
🤖 Method 5 — CI/CD Pipeline (Fully Automated)
# .github/workflows/optimize-images.yml
name: Auto-Optimize Images
on:
push:
paths:
- 'public/images/**'
jobs:
optimize:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install WebP tools
run: sudo apt-get install -y webp
- name: Convert images to WebP
run: |
find public/images \( -name "*.jpg" -o -name "*.png" \) | while read f; do
cwebp -q 80 "$f" -o "${f%.*}.webp"
done
- name: Commit optimized images
run: |
git config --global user.email "[email protected]"
git config --global user.name "Image Bot"
git add public/images/*.webp
git commit -m "🖼️ Auto-optimize images to WebP" || echo "Nothing to commit"
git push
📊 Method Comparison
| Method | Speed | Flexibility | Ease of Use | Production-Ready |
|---|---|---|---|---|
| Web tools | ⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐⭐ | ❌ |
| Bash + cwebp | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | ✅ |
| Node.js Sharp | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ✅✅ |
| Python Pillow | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ | ✅ |
| ImageMagick | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐ | ✅ |
| CI/CD Pipeline | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐ | ✅✅✅ |
🗂️ Recommended Output Structure
project/
├── images/ ← Original files (keep as backup)
│ ├── hero.jpg
│ ├── product-01.png
│ └── banner.gif
│
└── images-optimized/ ← Serve these on production
├── hero.avif ← Primary (best compression)
├── hero.webp ← Fallback
├── hero.jpg ← Final fallback
├── product-01.webp
└── banner.webp
HTML to serve them:
<picture>
<source srcset="/images-optimized/hero.avif" type="image/avif" />
<source srcset="/images-optimized/hero.webp" type="image/webp" />
<img
src="/images-optimized/hero.jpg"
alt="Hero image"
width="1200"
height="630"
fetchpriority="high"
/>
</picture>
✅ Batch Conversion Checklist
- [ ] Choose the right method for your project size
- [ ] Set compression quality to 80% (recommended default)
- [ ] Back up original images before converting
- [ ] Visually review a sample of converted images for quality
- [ ] Update your HTML to reference the new file formats
- [ ] Measure PageSpeed Insights before and after
- [ ] Automate the process in your CI/CD or build pipeline
- [ ] Set up the
<picture>element with proper fallbacks
🔗 Related Articles:
- WebP Guide ← Why WebP is the best output format
- Compression Guide ← Choosing the right quality setting
- PageSpeed Tips ← Maximizing the performance gain
