Keep P3
Compresses a Display P3 PNG without losing its colour profile, so a wide-gamut red survives the optimizer and the browser. Keep P3 is one of the Claude Code skills I wrote for building this site, with the site-specific values turned into placeholders and examples so it works on any project. The skill is the SKILL.md below, exactly as it ships in the zip.
Install
unzip keep-p3.zip -d ~/.claude/skillsThat installs it for every project. For one project, unzip into that project’s .claude/skills/ instead. Either way, Claude Code reads the folder on its next session and applies the skill when a request matches its description:
Compress a Display P3 PNG (an Icon Composer export, a screenshot from a P3 display) without losing its colour profile, so a wide-gamut red stays red in the browser. Explains why optimizers strip the profile, splices the Display P3 iCCP chunk back after compression, and covers where P3 survives on a site built with an image optimizer. Use when a PNG looks flat or desaturated after optimization, or when shipping an app icon or brand mark whose colour is outside sRGB.
Inside the zip
- —
keep-p3/SKILL.md· 3 KB - —
keep-p3/scripts/p3-png.mjs· 3 KB
The skill
A PNG’s pixels can be Display P3 while its label says nothing at all. Most image optimizers
(ImageOptim among them) strip every ancillary chunk to save bytes, and the colour profile
(iCCP) goes with the rest. The browser then reads the untagged file as sRGB, and a P3 red
that was outside the sRGB gamut comes back flat. There is no “keep profile” switch in ImageOptim.
The fix is not to skip optimization. Optimize, then put the label back: splice the Display
P3 profile in as an iCCP chunk right after IHDR. No pixel is touched; the pixels were P3
all along and only the tag was missing. scripts/p3-png.mjs in this skill does both steps:
it hands the file to ImageOptim.app, waits until the file has been rewritten and sits still,
then rewrites the chunk list, dropping any stale sRGB or iCCP chunk and inserting the
new one. The profile comes from macOS itself:
/System/Library/ColorSync/Profiles/Display P3.icc.
node scripts/p3-png.mjs icon@2x.png icon@1x.png
Only tag what is really P3
Tagging an sRGB file as P3 oversaturates it. Confirm the source first: an export from Icon Composer with the P3 option, a capture on a P3 display by a tool that preserves the profile, a render whose pipeline is P3. If in doubt, sample a known pixel with a colour picker in both readings; a P3 red reads roughly (197, 26, 10) where the sRGB version clips higher.
Where P3 survives on a site
Only in images served as they are. An image optimizer in the framework (next/image,
sharp-based pipelines) re-encodes the file and drops the profile again. Give such images a
way to bypass the optimizer (an unoptimized flag, a title token your image component
understands) and keep them small, since nothing will resize them. App icons, brand marks,
and small product shots are the usual cases; photographs rarely need it.
Check the result
exiftool or pngcheck lists the chunks: IHDR, then iCCP named Display P3, and no
sRGB chunk. In the browser, compare the served image against the source on a P3 display;
if the red is the same, the profile survived the whole path.