Brightness, saturation, and hue adjustment
These operations all change pixel appearance, but they are not interchangeable: brightness changes lightness, saturation changes colour intensity, and hue rotates a colour around the wheel. Each section covers one variant and pairs its image with the corresponding code.

Reference outputs were generated with govips v2.19.0 / libvips 8.17.3. Each binding has a corresponding output; pyvips and CLI saturation/hue files are currently unmodified placeholders marked not-supported.
Brightness adjustment
Section titled “Brightness adjustment”govips.Modulate(1.25, 1, 0) raises brightness in LCh; sharp uses HSL semantics. The pyvips and CLI snippets use RGB linear multiplication as an approximation, not a pixel-equivalent implementation.


Generated with govips v2.19.0 / libvips 8.17.3.
image, err := vips.NewImageFromFile("input.jpg")if err != nil { return err }defer image.Close()if err := image.Modulate(1.25, 1, 0); err != nil { return err }data, _, err := image.ExportWebp(vips.NewWebpExportParams())if err != nil { return err }return os.WriteFile("brightness.webp", data, 0o644)await sharp('input.jpg').modulate({ brightness: 1.25 }).webp({ quality: 82 }).toFile('brightness.webp');image = pyvips.Image.new_from_file("input.jpg")bright = image.linear([1.25, 1.25, 1.25], [0, 0, 0])bright.write_to_file("brightness.webp", Q=82)vips linear input.jpg 'brightness.webp[Q=82]' 1.25 0Saturation adjustment
Section titled “Saturation adjustment”govips and sharp expose a direct modulation entry point. The current pyvips and CLI paths preserve the input and mark the result not-supported: they run, but do not claim to have performed a saturation transform.


Generated with govips v2.19.0 / libvips 8.17.3.
if err := image.Modulate(1, 0.35, 0); err != nil { return err }await sharp('input.jpg').modulate({ saturation: 0.35 }).webp({ quality: 82 }).toFile('saturation.webp');# The Recipe marks this variant not-supported; do not call the copy an adjustment.image.write_to_file("saturation.webp", Q=82)# The Recipe marks this variant not-supported.vips copy input.jpg 'saturation.webp[Q=82]'Hue adjustment
Section titled “Hue adjustment”Hue is an angular offset; 60 means a 60-degree rotation. govips performs Modulate in LCh, while sharp uses HSL, so equal numbers do not guarantee equal pixels.


Generated with govips v2.19.0 / libvips 8.17.3.
if err := image.Modulate(1, 1, 60); err != nil { return err }await sharp('input.jpg').modulate({ hue: 60 }).webp({ quality: 82 }).toFile('hue.webp');# A manual LCh round-trip is required; the Runner does not implement it yet.image.write_to_file("hue.webp", Q=82)# Requires colourspace LCh → hue-channel adjustment → colourspace sRGB; not implemented yet.vips copy input.jpg 'hue.webp[Q=82]'