WebP lossy, lossless, near-lossless, and effort tuning
WebP combines VP8 intra-frame lossy compression and LZ77 + entropy lossless compression in one container, supporting an 8-bit alpha channel and multi-frame animation in both modes.


Comparing the three WebP encoding modes
Section titled “Comparing the three WebP encoding modes”Beyond standard Quality and Lossless, libvips exposes near-lossless mode (NearLossless), which is particularly effective on UI screenshots, charts, and icons:
- Lossy mode (
Lossless = false):Qualitycontrols VP8 quantization (default 75;78–85is typical for web photography). - Pure lossless mode (
Lossless = true): preserves exact pixel values;Qualitycontrols compression search effort rather than visual loss. - Near-lossless mode (
NearLossless = true): uses the lossless VP8L encoder after gently quantizing pixel deltas to improve LZ77 repeatability (Qualitysets the preprocessing precision, such as60or80), frequently cutting UI screenshot sizes by 30%–50% with negligible visual change.
| Setting | govips WebpExportParams |
sharp .webp(options) |
pyvips / vips CLI webpsave |
Notes |
|---|---|---|---|---|
| Quality / preprocessing | Quality (0..100) |
quality (default 80) |
Q=82 / --Q 82 |
Controls quantization in lossy mode or delta rounding in near-lossless mode |
| Pure lossless | Lossless: true |
lossless: true |
lossless=True / --lossless |
Enables exact VP8L lossless compression |
| Near-lossless | NearLossless: true |
nearLossless: true |
near_lossless=True / --near-lossless |
Pre-smooths pixels before lossless encoding; ideal for UI graphics |
| CPU effort | ReductionEffort (0..6, default 4) |
effort (0..6, default 4) |
effort=4 / --effort 4 |
0 is fastest; 4 is the production default; 6 is slowest |
| Alpha quality | — (does not expose alpha_q; default 100) |
alphaQuality (0..100) |
alpha_q=90 / --alpha-q 90 |
Setting 85–90 on lossy RGBA WebP shrinks translucent masks significantly |
| Smart chroma subsampling | — (uses libwebp default or Preset) |
smartSubsample: true |
smart_subsample=True |
Higher-quality YUV 4:2:0 chroma downsampling at extra CPU cost |
| Metadata retention | Keep (ForeignKeep*) |
.keepIccProfile() etc. |
keep="icc" / --keep icc |
Defaults differ by binding; govips v2.20 combines ICC, EXIF, XMP, IPTC, and other flags |
| Target byte size | TargetSize (bytes) |
— | target_size / --target-size |
Version-sensitive: requires libvips >= 8.17.4 |
1. Lossy photography vs near-lossless UI graphics
Section titled “1. Lossy photography vs near-lossless UI graphics”// Case A: Lossy WebP for photographs (govips v2.20.0 does not expose alpha_q; alpha uses default 100)lossy := vips.NewWebpExportParams()lossy.Quality = 82lossy.ReductionEffort = 4lossy.StripMetadata = truedata, _, err := image.ExportWebp(lossy)
// Case B: Near-lossless WebP for UI screenshots, diagrams, and badgesnearLossless := vips.NewWebpExportParams()nearLossless.NearLossless = truenearLossless.Quality = 60 // 60 applies moderate pixel preprocessing for higher lossless compressionnearLossless.ReductionEffort = 4nlData, _, err := image.ExportWebp(nearLossless)import sharp from 'sharp';
// Case A: Lossy WebP with smartSubsample for sharper coloured edgesawait sharp('input.jpg') .webp({ quality: 82, alphaQuality: 90, effort: 4, smartSubsample: true, }) .toFile('photo-lossy.webp');
// Case B: Near-lossless WebP for UI diagrams or iconsawait sharp('ui-diagram.png') .webp({ nearLossless: true, quality: 60, effort: 4, }) .toFile('ui-near-lossless.webp');import pyvips
image = pyvips.Image.new_from_file("input.jpg")
# Case A: Lossy WebP with smart chroma subsamplingimage.write_to_file( "photo-lossy.webp", Q=82, alpha_q=90, effort=4, smart_subsample=True, keep="icc",)
# Case B: Near-lossless WebPimage.write_to_file( "ui-near-lossless.webp", near_lossless=True, Q=60, effort=4,)# Case A: Lossy WebPvips webpsave input.jpg photo-lossy.webp --Q 82 --alpha-q 90 --effort 4 --smart-subsample
# Case B: Near-lossless WebPvips webpsave input.jpg ui-near-lossless.webp --near-lossless --Q 60 --effort 42. govips v2.20.0 Keep and WebP ICC
Section titled “2. govips v2.20.0 Keep and WebP ICC”WebpExportParams.Keep is a metadata bitmask. Zero means unset, so StripMetadata still decides whether metadata is stripped; an explicit Keep takes precedence:
params := vips.NewWebpExportParams()params.StripMetadata = trueparams.Keep = vips.ForeignKeepIccdata, _, err := image.ExportWebp(params)On the current v2.20.0/libvips 8.17.3 baseline, default WebP output retains the source ICC. ForeignKeepNone removes all metadata, while ForeignKeepIcc preserves ICC even with strip enabled. ForeignKeepGainmap requires libvips 8.18, so this baseline does not retain gain maps. Treat sharp, pyvips, and CLI switches and defaults independently.
3. TargetSize version boundary (libvips 8.17.4+)
Section titled “3. TargetSize version boundary (libvips 8.17.4+)”govips v2.19.0 adds TargetSize int to WebpExportParams (mapping to target_size in vips_webpsave) so libwebp can run multi-pass encoding to approach a target byte budget.