Skip to main content
convert is the explicit one: you say which files, which format, and what quality. For running a whole folder through sensible defaults, use optimize instead.

Arguments

string[]
required
What to convert. Takes any mix of files, directories (expanded recursively to the image files inside), and glob patterns — quote patterns so your shell doesn’t expand them first.
Names containing glob characters are handled literally when the file exists — holiday (1).png converts without any escaping.

Options

string
required
Target format, or several separated by commas: jpeg, png, webp, avif. Each input produces one output per format.
number
default:"82"
Encode quality from 1 to 100. Ignored for PNG, which is always lossless.
boolean
default:"false"
Use lossless encoding where the format supports it.
string
Directory to write results into. Defaults to writing next to each input file.
number
Scale so the longest side is at most this many pixels. Never upscales. Mutually exclusive with --resize-percent.
number
Scale to a percentage of the original dimensions. Mutually exclusive with --resize-max.
boolean
default:"false"
Keep EXIF/XMP in the output. Metadata is stripped unless you pass this.
boolean
Don’t apply EXIF orientation before encoding. By default photos are rotated upright first, so they stay correct once the metadata is gone.
string
default:"rename"
What to do when the output name already exists: rename, overwrite, or skip.
string
Write a JSON report of the results to this path.
boolean
default:"false"
Report what would be written without writing it.

Several formats at once

Pass a comma-separated list and each input produces one output per format — useful for shipping a modern format with a fallback:

Not overwriting things

By default a name collision gets a numeric suffix — photo.webp, then photo (1).webp. Your originals are never touched.
Adds (1), (2) and so on. Nothing is lost.
Replaces an existing output of the same name. Useful for repeatable builds where the output directory is disposable.
Leaves the existing file alone and reports the input as skipped. Good for resuming a large batch.

Dry runs

--dry-run does the full decode and encode — so the reported sizes are real — but writes nothing:

Manifests

--manifest writes a JSON report, which is what you’d parse in CI:
results.json
Each entry in files has:
string
required
The file that was read.
string
Where the result was written. Absent when the file failed.
string
required
One of succeeded, failed, or skipped.
number
required
Size of the original, in bytes.
number
Size of the result, in bytes. Absent when the file failed.
number
Fraction from 0 to 1. Positive means smaller — negative means the output grew.
string
Why the file failed. Present only on failure.
sizeReduction can be negative. Re-encoding an already-compressed file, or converting a flat graphic to a lossy format, can make it bigger — the report tells you rather than hiding it.

Metadata

EXIF/XMP is removed by default. Orientation is applied to the pixels first, so photos still come out upright:
This is the main thing the CLI can do that the browser can’t — canvas re-encoding always discards metadata as a side effect.