PNG compression levels, row filters, and palette quantization
PNG is often treated as an inflexible lossless format, yet libvips pngsave provides two distinct optimization levers:
- Truecolour lossless tuning: balancing zlib
Compression(0..9) and PNG row prediction filters (PngFilter) for speed vs size; - 8-bit indexed palette quantization (
Palette: true, similar to pngquant): quantizing 32-bit RGBA graphics into a 256-colour RGBA palette, typically shrinking UI icons, badges, and stickers by 60%–75%.

Cross-binding PNG parameter reference
Section titled “Cross-binding PNG parameter reference”| Setting | govips PngExportParams |
sharp .png(options) |
pyvips / vips CLI pngsave |
Tuning guidance |
|---|---|---|---|---|
| Compression level | Compression (0..9, default 6) |
compressionLevel (0..9, default 6) |
compression=6 |
4–6 for real-time HTTP pipelines; 9 for offline static asset builds |
| Row prediction filter | Filter (PngFilterNone / All) |
adaptiveFiltering (boolean) |
filter="none" / "all" |
None is faster and smaller on flat UI/palette graphics; All suits continuous-tone photos |
| Indexed palette mode | Palette: true |
palette: true |
palette=True / --palette |
Converts to indexed PNG8 (requires libimagequant or quantizr in libvips) |
| Palette colours / bitdepth | Bitdepth (1 / 2 / 4 / 8) |
colours (2..256) / bitdepth |
bitdepth (or colours) |
bitdepth=8 for up to 256 colours; bitdepth=4 for 16-colour icons |
| Quantization quality & dither | Quality (0..100), Dither (0..1) |
quality, dither, effort |
Q=85, dither=0.8, effort=7 |
Lowering Dither slightly improves deflate compression on flat colour fills |
| Adam7 interlacing | Interlace: true |
progressive: true |
interlace=True |
Avoid in production: Adam7 inflates PNG size by 10%–20% and slows decoding |
1. Truecolour lossless PNG and row filter selection
Section titled “1. Truecolour lossless PNG and row filter selection”Before zlib compression runs, PNG applies a scanline prediction filter (None, Sub, Up, Average, Paeth). On flat UI graphics, QR codes, line art, or indexed images, disabling adaptive filtering (PngFilterNone / adaptiveFiltering: false) encodes several times faster and often yields a smaller file:
params := vips.NewPngExportParams()params.Compression = 6params.Filter = vips.PngFilterNoneparams.Interlace = falseparams.StripMetadata = true
data, _, err := image.ExportPng(params)if err != nil { return err}return os.WriteFile("lossless.png", data, 0o644)import sharp from 'sharp';
await sharp('alpha-rings.svg') .png({ compressionLevel: 6, adaptiveFiltering: false, progressive: false, }) .toFile('lossless.png');import pyvips
image = pyvips.Image.new_from_file("alpha-rings.svg")image.write_to_file( "lossless.png", compression=6, filter="none", interlace=False, keep="none",)vips pngsave alpha-rings.svg lossless.png \ --compression 6 \ --filter none \ --keep none2. 8-bit indexed palette quantization (PNG8 with full alpha)
Section titled “2. 8-bit indexed palette quantization (PNG8 with full alpha)”When .png output is required (for legacy clients or email templates) and you want to shrink translucent badges, stickers, or UI overlays dramatically, enable Palette: true:
params := vips.NewPngExportParams()params.Palette = trueparams.Bitdepth = 8 // Up to 256 RGBA palette entriesparams.Quality = 85 // Quantization quality target (0..100)params.Dither = 0.8 // Floyd-Steinberg dithering strength (0.0..1.0)params.Compression = 6params.Filter = vips.PngFilterNone
data, _, err := image.ExportPng(params)if err != nil { return err}return os.WriteFile("palette-8bit.png", data, 0o644)import sharp from 'sharp';
await sharp('alpha-rings.svg') .png({ palette: true, colours: 256, quality: 85, dither: 0.8, effort: 7, compressionLevel: 6, }) .toFile('palette-8bit.png');import pyvips
image = pyvips.Image.new_from_file("alpha-rings.svg")image.write_to_file( "palette-8bit.png", palette=True, bitdepth=8, Q=85, dither=0.8, effort=7, compression=6, filter="none",)vips pngsave alpha-rings.svg palette-8bit.png \ --palette \ --bitdepth 8 \ --Q 85 \ --dither 0.8 \ --effort 7 \ --compression 6 \ --filter none