technical

Interactive post on OKLCH color space

Limitations of sRGB, and how OKlch makes color transitions smoother and more natural, just like the human eye perceives them.
Oct 13, 2024
15 min read

Introduction

The average person has three types of cones in their eyes that process color by separating light into red, green, and blue signals. These signals are then combined in the brain to create a complete visual picture.

Our eyes detect colors through these cones, each sensitive to different wavelengths of light. The exact number of colors we can distinguish is believed to be in the millions. This vast spectrum of colors is a fundamental part of how we experience the world, and we intuitively grasp how colors work. However, representing colors digitally is much more complicated, as common color spaces often face limitations, leading to inaccuracies or inconsistencies. Different color spaces are used to represent colors digitally. The most common ones are sRGB and HSL. In this article, I will be talking about the color space called OKlch. OKlch was introduced as part of the CSS Color 4 specification to improve color management in web design.

Warning: Your browser does not support OKLCH colors. Some colors may not display as intended.

sRGB Color Space implementation

The sRGB space is a standard RGB (red, green, blue) color space that HP and Microsoft created cooperatively in 1996 to use on monitors, printers, and the Web. sRGB stands for “Standard RGB”. It is the most widely used color space and is supported by most operating systems, software programs, monitors, and printers.

The most common implementation of sRGB is 24-bit color, where each color channel is given 8 bits, allowing 256 discrete levels (values from 0 to 255) for red, green, and blue. These combinations result in approximately 16.7 million colors. This implementation is used in browsers, image editing software, and other digital applications to represent colors on screens.

How 16.7 million colors?
Info

Each color channel in the sRGB color space (Red, Green, and Blue) is given 8 bits of data. Since 1 bit can represent 2 values (0 or 1), 8 bits can represent 28 = 256 discrete values. When combining the red, green, and blue channels, each of these 256 levels interacts with the others, allowing a total of: 256 x 256 x 256 = 16.7 million possible colors.

sRGB color space still cannot represent all visible colors. Although it can display millions of colors, its color gamut (see below) is limited compared to the full spectrum of colors the human eye can perceive, particularly in saturated or very light colors.

What is Color Gamut?
Info

A color gamut is the full range of colors that a device, system, or color model can show or capture. It sets the limits of the colors that can be displayed, printed, or recorded by things like monitors, printers, or cameras. Different devices and color spaces have different gamuts, meaning they can produce different ranges of colors.

In the below example, I have created a gradient using the full range of red, blue, and green colors. The gradient is linear and transitions from red to green to blue.

linear-gradient(to right, rgb(255, 0, 0), rgb(0, 255, 0), rgb(0, 0, 255))

sRGB attempts to combine red, green, and blue light to generate a wide range of colors, but it struggles with maintaining consistent brightness across certain hues. For example, you’ll likely notice that green appears significantly brighter than other colors. The result has muddier transitions in the middle range. This issue becomes even more noticeable when animating colors. Longer animation durations make the uneven transitions more pronounced and the uneven brightness level can be seen in the below example.

Transition Duration: 2sec
Click to animate color

Here you can see that the transition of the color change is not uniform and has a loss of color vibrancy.

Gradients between primary colors (red, green, or blue) and their derivatives are often more harsher. The human eye is more sensitive to certain colors (e.g., green) and less sensitive to others (e.g., blue), so changing RGB values leads to jumps or unevenness in the gradient. Below is an example of a gradient from yellow to blue using RGB.

linear-gradient(to right, rgb(255, 0, 0), rgb(0, 0, 255))

This is true if the colors are opposites on a color wheel. Such pairs are called Complementary colors in design. This phenomenon is due to the use of linear interpolation (also known as lerp) in the sRGB color space, which assumes uniform changes in values between colors. However, this doesn’t fully account for how we naturally perceive color shifts and transitions, because our perception is non-linear. Linear interpolation works as a good approximation only when the color values change at a constant rate, which may not always be the case.

What is interpolation?
Info

Interpolation is a mathematical method used to estimate unknown values that fall between known data points. In simple terms, it involves predicting a value within a certain range based on the values around it.

For example, if you know the temperatures at two points in time (say, 10 AM and 12 PM), you can use interpolation to estimate the temperature at 11 AM.

Point1
Point2
Notice how the gradient looks desaturated in the middle.

You can see how, in a typical sRGB color space, colors travel directly through the middle of the color space. As they pass through this center, the red, green, and blue components blend, which averages out the intensity of each component. This results in a more desaturated color at the midpoint—something grayish or muted.

However, when I created the custom gradient between those two points with 300 stops, it rendered as much brighter. This is because the center of many color wheel is often depicted as white because it represents the theoretical combination of all colors. And this gradient with 300 stops is passing through the center of the color wheel and not the color space.

A way to fix the desaturation when it goes through color space is by introducing a transitional color rather than allowing the gradient to pass directly through the center. Instead of traveling in a straight line from one color to another, we can create a more dynamic curve through color space, preserving brightness and vibrancy during the transition.

Point1
Point2
Notice how the gradient looks so clean.

This bend is what OKlch does. This was one of the motivations behind the creation of OKlch color space.

Blending two colors should result in even transitions. The transition colors should appear to be in between the blended colors (e.g. passing through a warmer color than either original color is not good).

Björn Ottosson (creator, oklch color space)

OKlch Color Space

Info

Björn Ottosson proposed OKlch in 2020 to create a color space that can closely mimic how color is perceived by the human eye, predicting perceived lightness, chroma, and hue.

A color in OKlch is represented with three coordinates, where the L axis represents lightness, the C axis represents chroma, and the H axis represents hue.

The OK in OKLCH stands for Optimal Color.

  • L: Lightness (the perceived brightness of the color)
  • C: Chroma (the intensity or saturation of the color)
  • H: Hue (the actual color, such as red, blue, green, etc.)

LCH has the below range:

ComponentDescriptionValue Range
LLightness0.0 to 1.0
CChroma (saturation)0.0 to 1.0
HHue (color type)0° to 360°

In the below example, I have set certain values for lightness and hue for every color. Chroma is kept constant at 0.25.

  • Orange (H: 45°): Increased lightness to 0.7 for a brighter perception.
  • Yellow (H: 90°): Increased lightness to 0.8 since yellow is perceived as very bright.
  • Cyan (H: 180°): Decreased lightness to 0.55, as cooler colors generally appear darker.
  • Blue (H: 270°): Kept lightness at 0.55 for consistency with cyan.
  • Red (H: 360°): Slightly increased lightness to 0.6 to enhance brightness without overwhelming the color.

This adjustment reflects a more accurate perception of brightness, aligning with how we visually interpret warmer and cooler colors.

90°180°270°

L: 0.7
C: 0.25
H: 45°

L: 0.8
C: 0.25
H: 90°

L: 0.55
C: 0.25
H: 180°

L: 0.55
C: 0.25
H: 270°

L: 0.6
C: 0.25
H: 360°

Colors with various lch values

Where is black?
Info

Black is not a color. It is absense of light. In OKlch, black is represented as L: 0, C: 0, H: (any value). In sRGB, black is represented as rgb(0, 0, 0).

OKlch is designed to be more perceptually uniform than other color spaces like sRGB or HSL. This means that small changes in the values of L, C, or H result in small, noticeable changes in color in a way how we perceive those colors, making it easier to manipulate and control colors accurately.

Below is a gradient from red to green using sRGB and OKlch color spaces.

From sRGB, I picked the highest point of red and green colors.
RGB
linear-gradient(to right, rgb(255 0 0), rgb(0 255 0))

Converting the above to OKlch
OKlch
linear-gradient(in oklch to right, rgb(255 0 0), rgb(0 255 0))

Here you can see the difference between the two color spaces in the middle where the transition happens. The OKlch color space has a smoother transition compared to sRGB.

Also, let’s compare the uniformity of a multicolored gradient. I have kept the lightness and chroma constant and only changed the hue in oklch. While in sRGB, I am changing the red, green, and blue values to get the equivalent colors.

/* oklch */
linear-gradient(
 to right,
 oklch(0.70 0.26 29),
 oklch(0.80 0.26 73),
 oklch(0.70 0.26 120),
 oklch(0.70 0.26 180),
 oklch(0.70 0.26 240),
 oklch(0.70 0.26 300)
)`;
/* rgb */
linear-gradient(
 to right,
 rgb(255, 0, 0),
 rgb(255, 127, 0),
 rgb(255, 255, 0),
 rgb(0, 255, 0),
 rgb(0, 0, 255),
 rgb(143, 0, 255)
)`;
RGB
Oklch
The OKLCH gradient maintains consistent perceived brightness and saturation across the spectrum, while the sRGB gradient may appear to have varying levels of brightness and saturation

Generating Color Palettes using OKlch Color Space

The OKlch color space is a cylindrical representation of colors, meaning it uses a three-dimensional coordinate system that resembles a cylinder. The surface around the cylinder represent the hue, its vertical axis represents lightness and its radius represents chroma. This allows us to create a wide range of colors with different lightness levels and intensity. By combining these three components, LCH can represent almost all possible colors that we can perceive. Thats why its easier to adjust and manipulate colors in an intuitive way that feels natural.

The below example shows that you can manipulate just 4 variables and an entire design system palette is automatically generated and applied.

oklch playground
Shadow
Accent

This is a subset of OKlch. All colors in this spectrum are bright. So what you are changing is the hue(h). I have limited the chroma(c) and lightness(l) to a particular range.

OKlch

Overall Color (Hue)

Band of colors. Pick a color and play with its intensity below.

Intensity (Chroma)

Chroma defines how much colorfulness a color has. High chroma values indicate highly saturated, vibrant colors.

Brightness (Lightness)

Lightness != Brightness but they are similar. Lightness is designed to be perceptually uniform, meaning that a change from 0.3 to 0.4 in lightness should result in a visually similar change as from 0.6 to 0.7

Compatibility and Usage

Modern displays are unable to reproduce the full spectrum of colors that the human eye can perceive. The standard color space currently in use is sRGB, which can display only 35% of the colors visible to us.

However, newer screens have improved this limitation by adding 30% of new colors, known as the P3 color space (or wide-gamut). In terms of usage, all Apple devices and many OLED screens now support the P3 color gamut. While P3 colors offer a wider color gamut, their representation is limited.

The color (display-p3 1 0 0) syntax, while functional, lacks the readability and interpretability of colors. Luckily, OKLCH has good readability, supports P3 and beyond, as well as any color visible to the human eye and as of today its supported in all modern browsers.

OKlch browser compatiability

Why did I build the OKlch color widget?

Short answer - For fun. Long answer -

I’ve been working with Tailwind, but I’ve always felt a bit frustrated with how I configured themes in my past projects. I’ve never liked the idea of hardcoding a palette from another design framework. While I utilize CSS variables with HSL or RGB colors, they lack the precision I need. When I tweak one color, it often leads to a ripple effect where I have to adjust other colors to keep everything looking cohesive.

Instead, I want to create a more dynamic color system that can automatically adapt whenever I change certain colors. I wanted the accent color to stand out. Since this site has both dark and light modes, it has to be even more dynamic. OKlch was something that just fits. I wanted others to play and see how it works.

Interesting facts about colors!
Info
  1. Blue is Rare in Nature: Unlike green, red, or brown, blue is one of the least common colors found in the natural world. Many creatures that appear blue, like butterflies or birds, actually don’t have blue pigments. Instead, they use structural color, where microscopic structures reflect blue light.
  2. Red is the First Color a Baby Sees: Newborns start seeing colors around two weeks old, and the first color they recognize is red, likely due to its longer wavelength and the way the human eye develops.
  3. Color Affects Taste Perception: The color of food can affect how we perceive its taste. For example, people often describe red or pink drinks as sweeter than they are, even if they’re not.
  4. The “Invisible” Color: In art and design, “invisible” colors refer to colors that are created through the interaction of visible colors. For example, the combination of certain colors can produce a color that is perceived but not physically present, like the color of light waves that mix.
  5. The Stroop Effect: This is a psychological phenomenon where people find it harder to name the color of a word when the word itself is the name of a different color. For example, if the word “blue” is written in red, it takes longer to identify the color red because of the conflict between the word’s meaning and its color.
  6. Red Light Preserves Night Vision: Red light is often used in military operations and for stargazing because it helps preserve night vision. The human eye is less sensitive to red wavelengths, allowing people to see in dim light without overwhelming their retinas and losing night vision.
  7. Humans Can See About a Million Different Shades: Our eyes can distinguish between approximately one million different shades of colors due to the combination of three types of cone cells in the retina that respond to different wavelengths of light.
  8. Color Blindness Doesn’t Mean Seeing Only in Black and White: Most people who are color blind still see colors, but they perceive certain colors differently. The most common type is red-green color blindness, where distinguishing between red and green is challenging.
  9. Black Isn’t a Color, It’s the Absence of Light: Black isn’t a color in the traditional sense because it doesn’t emit or reflect any light. It’s what we see when no visible light reaches our eyes. Similarly, white is the combination of all colors of light in the spectrum.
  10. There’s No Such Thing as “Magenta” in the Rainbow: Magenta doesn’t exist as a single wavelength of light like other colors in the rainbow. Our brain creates the color magenta by blending red and blue light when no green light is present.

Conclusion

OKlch operates in a perceptual color space, so transitions between colors are smoother and more visually accurate. When you interpolate between two colors in OKlch space, the resulting gradient respects how humans perceive changes in lightness, saturation (chroma), and hue. It’s not important to switch to OKlch but it’s important to understand how it works and what it unfolds. If you’re working on existing projects or systems that primarily use sRGB, switching to OKlch may introduce some complexity. sRGB is still the dominant color space in many applications. However, if you’re starting a new project especially those focused on color interactions (like art apps, design tools, etc.), adopting OKlch can future-proof your work.

References

Did you like the post?