Glossary

Color management

Color management is the system of tagging pixel values with a color space and converting them between devices so a color looks the same on a screen, a printer and a file. The common mechanism is the ICC profile, defined by the International Color Consortium. In web work you meet it as the color profile embedded in a JPEG, PNG or TIFF, and as the color spaces CSS can name.

How it works

Raw numbers such as rgb(255 0 0) mean nothing until they are tied to a space that says what red is. A profile supplies that link. ICC uses a reference colour space, the profile connection space, based on CIE XYZ or CIELAB. A source profile converts the file's values to that reference, and a destination profile converts from it to the output device.

A conversion also needs a rendering intent, which decides what happens to colors the destination cannot reproduce. The ICC specification defines four:

  • Perceptual: compresses the whole range to fit, trading accuracy for a natural look. Suited to photographs.
  • Media-relative colorimetric: keeps in-gamut colors exact relative to the white point and clips the rest. Suited to logos.
  • Saturation: favors vivid color over hue and lightness accuracy. Suited to charts.
  • ICC-absolute colorimetric: keeps absolute colorimetry, used for proofing one device on another.

The CSS Color 4 spec says an image is tied to a space by the format, usually through an embedded ICC profile. PNG also has a small sRGB chunk that tags an image as sRGB without a full profile, with a one-byte rendering intent. CSS itself converts colors you write in different spaces, for example color(display-p3 ...), to the display's space.

import struct, zlib
def chunk(t, d): return struct.pack('>I', len(d)) + t + d + struct.pack('>I', zlib.crc32(t + d))
ihdr = struct.pack('>IIBBBBB', 1, 1, 8, 2, 0, 0, 0)
idat = zlib.compress(b'\x00\xff\x00\x00')
def build(extra): return b'\x89PNG\r\n\x1a\n' + chunk(b'IHDR', ihdr) + extra + chunk(b'IDAT', idat) + chunk(b'IEND', b'')
names = {0: 'perceptual', 1: 'relative colorimetric', 2: 'saturation', 3: 'absolute colorimetric'}
files = {'untagged.png': build(b''),
         'srgb.png': build(chunk(b'sRGB', bytes([0]))),
         'relative.png': build(chunk(b'sRGB', bytes([1])))}
for fn, data in files.items():
    pos, out = 8, []
    while pos < len(data):
        n = struct.unpack('>I', data[pos:pos+4])[0]; t = data[pos+4:pos+8].decode(); body = data[pos+8:pos+8+n]
        out.append('sRGB intent=%d (%s)' % (body[0], names[body[0]]) if t == 'sRGB' else t)
        pos += 12 + n
    print(fn.ljust(14), ', '.join(out))
untagged.png   IHDR, IDAT, IEND
srgb.png       IHDR, sRGB intent=0 (perceptual), IDAT, IEND
relative.png   IHDR, sRGB intent=1 (relative colorimetric), IDAT, IEND

Why do colors look different in my browser than in my design tool?

The two apps are usually interpreting the same numbers in different spaces. A design tool working in Display P3 and a screenshot treated as sRGB will disagree on saturated colors. Fix it by exporting with a profile or converting to sRGB before export, and by checking that both apps use a profile.

Common pitfalls

  • Stripping the profile to save bytes: an optimizer that removes the embedded ICC profile can turn a wide-gamut photo dull. Convert to sRGB first, then strip.
  • Converting twice: applying a profile to pixels that were already converted shifts colors again. Keep track of the current space.
  • Using the wrong rendering intent: perceptual on a flat logo changes brand colors that relative colorimetric would keep.
  • Assuming untagged means correct: an untagged image has no stated space, so each app has to guess one.
  • Mixing print and screen assumptions: a CMYK file needs a print profile, and the CMYK numbers do not map to RGB without one.

Related terms

  • sRGB — the default space for untagged web images
  • Color gamut — the range of colors a profile describes
  • RGB — the model most screen profiles describe
  • CMYK — the print model that depends on profiles to convert
  • Lab color — a device-independent space ICC uses as a reference

See also

  • Term: Color gamut — why a conversion can leave colors out of range