If you hand images over to clients, sooner or later someone says the product shots on the new site look washed out compared to the files they sent. Nothing is blurry and the dimensions are right, so the first suspect is compression quality. Raising the WebP quality usually changes nothing, because the pixels were never the problem. What went missing is the ICC color profile, the small block of data that tells software which color space the RGB numbers belong to. Lose a Display P3 or Adobe RGB profile and every viewer falls back to reading the same numbers as sRGB, which makes saturated colors duller.
To see how much that matters, I took a CC0 photo from Wikimedia Commons (an airport apron shot on an iPhone 6), converted it on my Mac into a Display P3 JPEG and an Adobe RGB JPEG with the profiles embedded, then compared each one displayed correctly against the same pixels read as sRGB. On this sample, dropping the P3 profile cut average chroma by 16.6% (mean CIELAB ΔE76 of 1.83), and dropping Adobe RGB cut it by 20.1% (mean ΔE 2.91, with 7.4% of pixels above ΔE 5). Whites, greys and shadows barely moved; the red tail fin and the sky took the hit. The photo's colors all sit inside sRGB, so I can't tell you what happens to a genuinely wide-gamut image. Apple's developer documentation notes that iPhone cameras have been able to capture P3 since the iPhone 7, so client files carrying a non-sRGB profile are normal. Below is the checklist I now run before a delivery goes out.
1. Check what profile the originals carry
On macOS, sips -g profile photo.jpg prints the profile name, so you'll see Display P3 or Adobe RGB (1998) when one is embedded. If you'd rather not open a terminal, the profile is usually listed in Preview's inspector or the file info panel. One thing surprised me: for a JPEG with no embedded profile at all, sips still prints sRGB IEC61966-2.1. So "sRGB" from sips doesn't prove a profile exists. It only means the file will be treated as sRGB, which is harmless. The originals that report P3 or Adobe RGB are the ones to watch.
2. Check whether the profile survived export
I fed the same P3 JPEG through a few common tools with default settings:
- Pillow 11.3 dropped the profile when saving JPEG and WebP, and also after resize or thumbnail followed by a JPEG save. PNG kept it. Passing the original icc_profile to save() keeps it for JPEG.
- ffmpeg 7.1 kept it for JPEG and PNG, and dropped it for WebP.
- macOS sips kept it when converting to PNG, resizing, and saving JPEG at quality 60. My attempt to write WebP with sips produced no file, so no verdict there.
WebP is the format where both Pillow and ffmpeg drop the profile by default, which is probably why "WebP makes colors dull" gets repeated so often. Run sips again on the output. A WebP without a profile prints nil, which is easy to spot. A JPEG without one prints sRGB, so compare against what the original reported.
3. Look at them side by side in a browser
I put the original, the exported file and a profile-stripped copy on one test page. In the Chromium 149 and WebKit 26.5 engines bundled with Playwright (macOS 26.5), the tagged and untagged images rendered differently, so both engines honor the profile and treat untagged images as sRGB. The difference shows up in saturated areas. In Playwright's own Firefox 151 build the two looked identical. That only reflects that build's defaults, and I haven't tested release Firefox.
What I deliver now
For images going onto a website, I convert to sRGB and embed the sRGB profile before compressing. If some later step (a CDN, a client's upload widget, another script) strips the profile, browsers still read the pixels as sRGB and the colors hold. The trade-off is that any colors outside sRGB get mapped into it. I have no sample to measure that loss on real wide-gamut photos yet. When a client explicitly needs wide gamut, I keep the original profile instead, use tools I've confirmed preserve it, and spot-check one file per batch with sips.

Top comments (0)