Batch Convert JPG to WebP: cwebp, ImageMagick, FFmpeg, sharp, and Build Pipeline Automation

Converting a single JPG to WebP takes seconds in any online converter. But converting an entire website — hundreds or thousands of images — requires automation. This guide covers every method from quick shell commands to full CI/CD pipelines, with copy-paste scripts for each tool.

Why Batch Conversion Matters

  • A typical e-commerce site has 5,000–50,000 product images — manually converting one by one is not feasible at any scale
  • Batch tools take advantage of multi-core CPUs, converting dozens of images simultaneously
  • Integrating into build pipelines means every new image is automatically converted without any manual step
  • Cost savings are real: serving WebP instead of JPEG typically reduces image bandwidth by 25–35%, which translates directly to lower CDN bills and faster pages
  • PageSpeed Insights flags JPEG images in its "Serve images in next-gen formats" audit — batch conversion resolves this site-wide in a single command
ToolBest forParallel?Build integration
cwebpQuick local batch, shell scriptsVia xargs -PManual / Makefile
ImageMagickResize + convert in one stepVia mogrifyManual / Makefile
FFmpegAlready using FFmpeg in pipelineVia shell loopManual
sharp (Node.js)JS projects, custom pipelinesNative (Promise.all)npm scripts, Vite, Next.js
vite-plugin-imageminVite projectsYesAutomatic on build
GitHub ActionsAuto-convert committed imagesYesFull CI/CD

Method 1 — cwebp (Google's Official CLI)

cwebp is Google's reference encoder for WebP. It produces the highest-quality WebP output and is the tool to reach for when you need maximum control over encoding parameters.

Install

# macOS
brew install webp

# Ubuntu / Debian
sudo apt install webp

# Windows — download from https://developers.google.com/speed/webp/download

Convert a single file

cwebp -q 82 photo.jpg -o photo.webp

The -q flag sets quality (0–100). 80–85 is the recommended range for photographs — visually indistinguishable from the source at 25–35% smaller file size.

Batch convert all JPGs in a directory

# Linux / macOS
for f in *.jpg; do
  cwebp -q 82 "$f" -o "${f%.jpg}.webp"
done

# Also convert PNGs (use lossless for graphics)
for f in *.png; do
  cwebp -lossless "$f" -o "${f%.png}.webp"
done

Batch convert recursively (all subdirectories)

find . -name "*.jpg" -exec sh -c \
  'cwebp -q 82 "$1" -o "${1%.jpg}.webp"' _ {} \;

Parallel conversion using xargs (much faster)

find . -name "*.jpg" | xargs -P 8 -I{} sh -c \
  'cwebp -q 82 "$1" -o "${1%.jpg}.webp"' _ {}
# -P 8 runs 8 parallel processes — adjust to match your CPU core count

Using xargs -P 8 on an 8-core machine will use all available cores simultaneously, typically reducing total batch time by 6–7× compared to the sequential loop.

No Time to Set Up CLI Tools?

Don't have time to set up CLI tools? Convert JPG to WebP instantly in your browser — no upload, free, unlimited.

Method 2 — ImageMagick

ImageMagick's mogrify command can batch-process an entire directory in a single command, and it supports combining conversion with resize, crop, or other transformations in one pass.

Install

brew install imagemagick   # macOS
sudo apt install imagemagick   # Ubuntu / Debian

Convert a single file

magick photo.jpg -quality 82 photo.webp

Batch convert all JPGs in current directory

magick mogrify -format webp -quality 82 *.jpg
# WARNING: mogrify modifies files in-place. Use -path to output to a separate directory:
magick mogrify -format webp -quality 82 -path ./webp/ *.jpg

Always specify -path ./webp/ to direct output to a dedicated folder. Running mogrify without -path overwrites the source files with WebP — you lose the original JPGs.

Convert with resize in one step

magick mogrify -format webp -quality 80 -resize 800x600> -path ./webp-resized/ *.jpg
# The > after dimensions means "only shrink, never enlarge"

This is one of ImageMagick's key advantages: generating WebP thumbnails in a single command instead of requiring a separate resize step.

Method 3 — FFmpeg

FFmpeg is primarily a video tool but handles image conversion reliably and is often already installed in environments that process media.

# Single file
ffmpeg -i photo.jpg -q:v 80 photo.webp

# Batch (Linux/macOS)
for f in *.jpg; do
  ffmpeg -i "$f" -q:v 80 "${f%.jpg}.webp"
done

Note on quality scale: FFmpeg's -q:v for WebP maps differently from cwebp — lower values produce better quality (0 = best, 100 = worst), the opposite of cwebp's scale. Use -q:v 20 for high quality or -q:v 80 for aggressive compression. If you are already using FFmpeg in your pipeline, this avoids installing an additional tool — but for dedicated image batch work, cwebp or sharp will give you more control.

Method 4 — Node.js with sharp (Recommended for Projects)

sharp is the fastest Node.js image processing library, built on libvips. It handles parallel processing natively, integrates with any JavaScript build system, and allows quality logic per image. For any project with a package.json, sharp is the recommended approach.

Install

npm install sharp

Basic conversion script

// convert-to-webp.js
const sharp = require('sharp');
const { glob } = require('glob'); // npm install glob

async function convertToWebP(inputGlob, quality = 82) {
  const files = await glob(inputGlob);
  await Promise.all(files.map(async (file) => {
    const out = file.replace(/\.(jpe?g|png)$/i, '.webp');
    const isPhoto = /\.(jpe?g)$/i.test(file);
    await sharp(file)
      .webp({ quality: isPhoto ? quality : undefined, lossless: !isPhoto })
      .toFile(out);
    console.log(`Converted: ${file} -> ${out}`);
  }));
}

convertToWebP('**/*.{jpg,jpeg}', 82);

Run: node convert-to-webp.js

Promise.all() converts all matched files in parallel, fully utilizing available CPU cores. On a machine with 8 cores, a folder of 500 images converts significantly faster than any sequential shell loop.

Add to package.json

{
  "scripts": {
    "webp": "node convert-to-webp.js",
    "build": "npm run webp && vite build"
  }
}

With this setup, every production build automatically generates up-to-date WebP versions before the Vite build runs.

Method 5 — Vite Plugin (vite-plugin-imagemin)

If your project uses Vite, you can make WebP conversion part of the build with zero manual steps. Every image in your src/ directory is automatically converted and optimized during vite build.

Install

npm install vite-plugin-imagemin --save-dev

vite.config.js

import { defineConfig } from 'vite';
import viteImagemin from 'vite-plugin-imagemin';

export default defineConfig({
  plugins: [
    viteImagemin({
      webp: { quality: 82 },
      optipng: { optimizationLevel: 5 },
      mozjpeg: { quality: 80 },
    }),
  ],
});

The plugin runs during the Vite build pipeline, so CI/CD environments get WebP output automatically without any additional script step. The original source images in src/ remain untouched; only the built output in dist/ contains the WebP versions.

Method 6 — GitHub Actions (CI/CD Automation)

Auto-convert any new JPG committed to your repository. Every time a developer pushes a JPG, the action converts it to WebP and commits the result back — no developer needs cwebp installed locally.

.github/workflows/convert-webp.yml

name: Convert JPG to WebP

on:
  push:
    paths:
      - '**/*.jpg'
      - '**/*.jpeg'

jobs:
  convert:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install WebP tools
        run: sudo apt-get install -y webp

      - name: Convert new JPGs to WebP
        run: |
          find . -name "*.jpg" -newer .last-webp-build | while read f; do
            cwebp -q 82 "$f" -o "${f%.jpg}.webp"
            echo "Converted: $f"
          done
          touch .last-webp-build

      - name: Commit WebP files
        uses: EndBug/add-and-commit@v9
        with:
          message: 'Auto-convert JPG to WebP [skip ci]'
          add: '*.webp'

The [skip ci] suffix in the commit message prevents the action from triggering itself in a loop. The -newer .last-webp-build flag ensures only images added or modified since the last run are processed, keeping the action fast even in large repositories.

Method 7 — Watch Mode for Development

During development, auto-convert on every save using chokidar. This means your dev server always serves the latest WebP version alongside the source JPG without any manual step.

Install

npm install chokidar sharp --save-dev

watch-webp.js

const chokidar = require('chokidar');
const sharp = require('sharp');

chokidar.watch('src/**/*.{jpg,jpeg}').on('add', convert).on('change', convert);

async function convert(filePath) {
  const out = filePath.replace(/\.(jpe?g)$/i, '.webp');
  await sharp(filePath).webp({ quality: 82 }).toFile(out);
  console.log(`[webp] ${filePath} -> ${out}`);
}

Add to package.json: "dev:webp": "node watch-webp.js" and run it alongside your dev server. Chokidar uses native OS file-system events (inotify on Linux, FSEvents on macOS) — it does not poll the disk, so CPU usage is near zero when no files are changing.

Quality Strategy by Directory

Not all images deserve the same quality setting. Product photos on a detail page need higher fidelity than thumbnail previews or background textures. A directory-based quality strategy lets you optimize for both file size and visual quality in one script.

#!/bin/bash
# convert-by-type.sh

convert_dir() {
  local dir="$1" quality="$2"
  find "$dir" -name "*.jpg" | xargs -P 4 -I{} sh -c \
    'cwebp -q '"$quality"' "$1" -o "${1%.jpg}.webp"' _ {}
  echo "Converted $dir at quality $quality"
}

convert_dir "./images/products"    85
convert_dir "./images/heroes"      83
convert_dir "./images/thumbnails"  75
convert_dir "./images/backgrounds" 70

# Lossless for icons and UI elements (PNG source)
find ./images/icons -name "*.png" -exec sh -c \
  'cwebp -lossless "$1" -o "${1%.png}.webp"' _ {} \;

Run this script from your project root: bash convert-by-type.sh. Add it to your Makefile or npm scripts as a pre-build step. The -P 4 flag runs 4 parallel processes per directory — increase to match your CPU count for faster results.

Quality reference

Image typeRecommended qualityReason
Product detail photos83–85Users inspect closely; artifacts are noticeable
Hero / banner images80–83Large size means even small % savings are significant KB
Article inline images78–82Medium fidelity acceptable; text context reduces perception of artifacts
Thumbnails / cards72–77Small display size masks compression artifacts
Background textures65–72Decorative; heavy compression acceptable
Icons / logos (PNG source)losslessHard edges and flat colour require lossless to avoid ringing

Frequently Asked Questions

What is the fastest way to batch convert JPG to WebP?
For a folder of images on your computer, cwebp with a parallel xargs loop is the fastest: find . -name "*.jpg" | xargs -P 8 -I{} sh -c 'cwebp -q 82 "$1" -o "${1%.jpg}.webp"' _ {}. For automated pipelines, the Node.js sharp library processes images in parallel via Promise.all() and integrates directly with build tools like Vite, webpack, and Next.js.
Does batch converting to WebP replace the original JPG files?
By default, no — tools like cwebp and ImageMagick create new .webp files alongside the originals. The one exception is magick mogrify without a -path argument, which overwrites in place. Always keep your original JPGs as the source of truth. WebP files should be your delivery format, not your archive format — source files should always be lossless or high-quality originals.
How long does it take to batch convert 1000 images to WebP?
With cwebp at quality 80 on a modern CPU: roughly 2–4 seconds per image single-threaded, or under a second per image using parallel processing with xargs -P or sharp's Promise.all(). 1,000 images typically takes 2–10 minutes depending on image resolution (a 4 MB DSLR RAW-to-JPEG takes longer than a 150 KB product thumbnail) and the number of CPU cores available.
Can I batch convert with quality settings per image type?
Yes — write a shell script that applies different quality levels based on file path or directory. For example, quality 85 for /products/, quality 75 for /thumbnails/, and lossless for /icons/. The sharp Node.js library makes this particularly easy: in a JavaScript pipeline you can inspect the file path and set quality per image in a single Promise.all() pass.
Do I need to regenerate WebP files when I update source JPGs?
Yes. WebP files are derivatives of your source images. If you update a source JPG and do not regenerate the WebP, your site will serve a stale WebP that no longer matches the current source. Set up a watch script during development (e.g. chokidar + sharp) and add WebP generation to your CI/CD pipeline so every new or updated JPG automatically gets a WebP version built and deployed alongside it.
Convertlo Editorial Team
The Convertlo team writes practical guides on image formats, file conversion, and web performance. All technical content is verified against current format specifications and tested in real browsers.
convertlo.pro