For the longest time, color profiles have been poorly supported by web browsers:

  • MacOS (provided you have a decent monitor) has been able to display images with different color profiles such as “Adobe RGB” or “DCI-p3”, which allows to display a wider gamut of colors (HDR) than the standard “sRGB” profile.
    • This also applied to images on web pages, especially when using Safari.
  • Windows and Linux did not care much and have mostly ignored color profiles until recently.
    • This also applied to Chrome, Firefox and IE / Edge.
    • Those browsers have ignored color profiles in images and just assumed they are “sRGB” images and displayed them as such.

Left: image properly displayed with Adobe RGB profile; Right: image displayed as sRGB without proper conversion :(

The problem is that, when a browser displays a wide gamut image as sRGB, without doing any proper conversion, it results in a very dull, toned down, greyish image. It loses the original vibrancy of the colors, even when those colors would have been part of the sRGB spectrum.

I recently noticed it on my home page. See comparative screenshot above.

For that reason, a “best practice” has been to stick to sRGB for the any web images… Technically you could use <picture> to specify various sources like <source srcset="image-wide-gamut.jpg" media="(color-gamut: p3)"> and let the #browsers choose depending on the color-gamuts they can actually display. Unfortunately support for that is still a bit unpredictable on Android, and of course the old IE… (Also, see below)

As of today, most browsers recognize color profiles in images, although not perfectly (Firefox requires a setting to be turned on; others won’t work properly when the user has multiple different monitors…)

Nevertheless, it started making sense to use wide gamut color profiles, especially as photos that come out of the iPhone (and most other digital cameras) have been using wide gamut profiles for many years now. Why convert them down to a more limited sRGB profile when people who care about colors probably have a setup to display them correctly?

Then comes #hugo

I really like hugo for generating static web sites. I use it as part of my #obsigo workflow. It does many great things. One of these is that it automatically resizes images for efficient responsive web design!

Only problem: when it resizes, it royally ignores the color profile and the downsized images have no color profile any more. Browsers will resort to displaying them as sRGB, without any proper conversion, and duh… you get the dull colors again!

It seems that hugo can’t do much about it because the non-support of color profiles lies in the underlying #golang imaging library.

This also kills any hope of using media="(color-gamut: p3)"as hugo won’t be able to make responsive (multiple sizes) of that.

Brute force solution

Go back to maintaining your content like it’s 2009 and down-convert all your images to sRGB right when you create them.

A better solution

Given that:

  • I have hundreds of images that use various wide gamut profiles (depending on which camera they come from) and…
  • I want to actually keep the originals as wide gamut (also in hope that in some not too distant future all browsers properly support profiles) and..
  • With #obsigo, I already copy images from the “source content” folder to the “hugo content” folder..

–> I figured I need to transparently convert the color profiles when copying over the images files.

I don’t know exactly how I’m going to do it yet, but I found this great tool to help me:

ImageMagick

On mac, install with #homebrew: brew install imagemagick

You can check the color profile of an image file like this:

$ magick identify -format "%f: %[profile:icc]\n" fp.jpg                                             

fp.jpg: Adobe RGB (1998)

This is the same result as what MacOS Preview would should you:

Then you can properly convert to sRGB like this:

$ magick fp.jpg -profile "/System/Library/ColorSync/Profiles/sRGB Profile.icc" fp_sRGB.jpg

Note: the .icc profile used here comes by default on MacOS. But you could also provide your own .icc profile.

Then check:

$ magick identify -format "%f: %[profile:icc]\n" fp_sRGB.jpg

And check in Preview:

This resulting image finally processes correctly in hugo…

Bonus feature #1: WebP

At the same time we can convert to the WebP format with better quality compression:

magick fp.jpg -profile "/System/Library/ColorSync/Profiles/sRGB Profile.icc" -quality 92 fp_sRGB92.webp

Note: a WebP compression of quality of 92 seems to be the sweet spot for me, for large images and with imagemagick (I am not sure this translates to other sizes and other compression libraries.)

With WebP the file size is smaller and the quality remains roughly the same.

Note: You can’t really compare the compression quality on these screenshots as they have been recompressed, so you’ll have to take my word for it (or make your own tests ;): smaller file size for roughly equivalent quality to JPEG.

Hugo again :/

This is all nice… until you try to resize those WebP images with Hugo again… Whereas hugo did a great job resizing sRGB JPEG image, it manages to screw up the colors again when resizing sRGB WebP images! :( The resulting images have no embedded/declared color profile at all, and they will display faded out again (at least on #safari and #macos Preview).

But… hugo again has some quirks resulting from the fact it uses different libraries for WebP and for other image formats. The bug is when it reads it WebP, not when it writes WebP. So you can still get it to work by using jpeg for originals in your content and generating WebP in hugo.

Example in Hugo:

{{- $img_sized := $img.Resize (printf "%dx webp q85" $width) }}

Note: the resulting .webp images from hugo don’t declare a color profile whereas .jpegimages created by hugo did declare a color profile (sRGB as desired).

TODO: more testing, as it remains unclear that hugo will produce good quality images at a size below jpeg.

In the meantime:

Bonus feature #2: HEIC

In addition to using wide gamut color profiles, photos from iPhones and many other now come in HEIC format, which is probably even better than WebP at compressing more without losing quality.

However, neither Hugo nor web browsers outside Safari support it.

It is however possible to use HEIC for the source content images:

  • Obsidian can display it with a plugin called “Image Magician” (it uses Image Magick !)
  • We can convert from HEIC to JPEG at the same time we convert color profiles:
magick IMG_5188.heic  -profile "/System/Library/ColorSync/Profiles/sRGB Profile.icc" IMG_5188.jpeg

Conclusions

For now my conclusions are that, if working with hugo, I need to:

  • Keep my source content images in JPEG or HEIC
  • Transcode those source images to sRGB JPEGs prior to running Hugo
  • (Pending more tests) Not bother trying to convert to WebP when generating responsive sizes in hugo

(All in all, not a great score card for hugo…)