Why CMY Works Better Than RGB in Paint Mixing

Why CMY Works Better Than RGB in Paint Mixing
Photo by Elena Mozhvilo / Unsplash

The colors on an LCD computer monitor are red, green, and blue (RGB). However, in acrylic painting, the primary colors are red, yellow, and blue (RYB). Why does green get replaced by yellow when mixing paints?

Anatomy of the Eye

Every photon that enters the eye excites one of the retina's photoreceptors, generating an electrical signal. The brain's visual cortex processes these signals to create the perception of shapes and colors. Humans typically have three types of cone cells, each sensitive to different wavelengths of light:

  • L (long): Peaks at 560 nm (red)
  • M (medium): Peaks at 530 nm (green)
  • S (short): Peaks at 420 nm (blue)

The brain interprets the signals from these cones, giving rise to what we perceive as red, green, and blue. However, it’s impossible to truly know how "red" feels to another person because these sensations exist entirely within the brain.

Describing Light

To simplify, we can model light perception using three unit vectors representing RGB photons and their respective intensities. The perceived light can be expressed as:

x = a_r * u_r + a_g * u_g + a_b * u_b

Assuming the cone response is unity, the coefficients in the light space represent the perceived color. We define our light source as having equal unit intensity for each color component:

source = 1 * u_r + 1 * u_g + 1 * u_b

Absorption of Light

To create color, certain colored photons must be absorbed from the light source while allowing the remaining photons to pass through, similar to the effect of tinted glass. This process can be modeled as a function that maps a vector in light space back into itself. Assuming the operation is linear, it can be represented by a diagonal matrix, derived by subtracting the absorption matrix from the identity matrix:

tint = diag(1, 1, 1) - diag(a_absorb_r, a_absorb_g, a_absorb_b)

For pure red, green, or blue tints, the matrices would look like this:

tint_r(tint_intensity) = diag(1, 0, 0)
tint_g(tint_intensity) = diag(0, 1, 0)
tint_b(tint_intensity) = diag(0, 0, 1)

Reflection can be modeled as a perfect identity matrix:

reflect = diag(1, 1, 1)

Attenuation, which reduces light intensity, is represented as:

attenuate = attenuate_intensity * diag(1, 1, 1)

LCD Monitor Mechanics

An LCD monitor uses a backlight emitting white light. Each pixel has three channels (red, green, and blue), with components to tint and attenuate the light. The desired brightness of each color is controlled by the attenuator.

┌────────────┐       ┌──────────┐        ┌───────────────┐        
│            │       │          │        │               │        
│            │ ─────►│  tint_r  │ ─────► │  attenuate_r  │ ──────► 0
│            │       │          │        │               │        
│            │       └──────────┘        └───────────────┘        
│            │       ┌──────────┐        ┌───────────────┐        
│            │       │          │        │               │        
│   source   │ ─────►│  tint_g  │ ─────► │  attenuate_g  │ ──────► 0
│            │       │          │        │               │        
│            │       └──────────┘        └───────────────┘        
│            │       ┌──────────┐        ┌───────────────┐        
│            │       │          │        │               │        
│            │ ─────►│  tint_b  │ ─────► │  attenuate_b  │ ──────► 0
│            │       │          │        │               │        
└────────────┘       └──────────┘        └───────────────┘        

The brightness of each color is determined by the attenuate_intensity applied to the liquid crystals. One-third of the source light passes through each channel.

output = 1/3 * tint_r * attenuate(attenuate_intensity_r) * source +
         1/3 * tint_g * attenuate(attenuate_intensity_g) * source +
         1/3 * tint_b * attenuate(attenuate_intensity_b) * source
       = (attenuate_intensity_r * tint_r +
          attenuate_intensity_g * tint_g +
          attenuate_intensity_b * tint_b) / 3 * source

-> 0000000000000000

For example, when displaying pure red at full brightness (100% red), the output can be modeled as:

output = tint_r / 3 * source
       = diag(1/3, 0, 0) * source
       = 1/3 * u_r

The result, as expected, is red. However, it has only one-third of the source's red energy since the remaining two-thirds were allocated to the other colors, which were attenuated.

-> 0000000000000000

For pure white, the output combines all three channels equally:

output = (tint_r + tint_g + tint_b) / 3 * source
       = diag(1, 1, 1) / 3 * source
       = 1/3 * u_r + 1/3 * u_g + 1/3 * u_b

Similarly, pure white will still retain only one-third of the source's total energy.

-> 0000000000000000

Yellow is produced by combining red and green light:

output = (tint_r + tint_g) / 3 * source
       = diag(1/3, 1/3, 0) * source
       = 1/3 * u_r + 1/3 * u_g

While the LCD can display any color in the RGB spectrum, each color contains at most one-third of the energy of the light source.

Acrylic Painting Mechanics

In acrylic painting, the source light shines onto the white canvas, which acts as a reflector. The light passes through the paint pigments both before and after reflection, with the pigments absorbing specific photons to produce visible color. Because the pigments are thoroughly mixed, the light is tinted by all the pigments in the mixture. In practice, not all pigments in a mixture retain the same effectiveness as in their pure form, as they tend to dilute each other. However, in the case of acrylic paints, the pigment concentration is generally low, making the diluting effect negligible.

┌────────────┐         ┌────────────┐       ┌──────────┐        ┌──────────┐        
│            │         │            │       │          │        │          │        
│            │ ──────► │            │ ─────►│          │ ─────► │          │ ──────►
│            │         │            │       │          │        │          │        
│            │         │            │       │          │        │          │        
│            │         │            │       │          │        │          │        
│            │         │            │       │          │        │          │        
│   source   │ ──────► │   reflect  │ ─────►│   tint   │ ─────► │   tint   │ ──────►
│            │         │            │       │          │        │          │        
│            │         │            │       │          │        │          │        
│            │         │            │       │          │        │          │        
│            │         │            │       │          │        │          │        
│            │ ──────► │            │ ─────►│          │ ─────► │          │ ──────►
│            │         │            │       │          │        │          │        
└────────────┘         └────────────┘       └──────────┘        └──────────┘        

The tints are applied sequentially, and there is no explicit method for attenuation. Instead, brightness is typically controlled using dark colors, which can also be modeled as a form of tinting.

output = reflect * tint * tint * ... * source
       = tint * tint * ... * source

-> 0000000000000000

The simplest case occurs when we paint red using red paint (or any pure color):

output = tint_r * source
       = diag(1, 0, 0) * source
       = u_r

The result, unsurprisingly, is still red, but it includes all of the red photons from the source light, as the light wasn't divided or attenuated like it is in LCD monitors.

-> 0000000000000000

Now, let's get creative! We'll mix pure red and green to create a yellow color:

output = tint_r * tint_g * source
       = diag(1, 0, 0) * diag(0, 1, 0) * source
       = 0

Oops, we ended up with black instead of yellow. Imagine passing light through a red tint that absorbs all green and blue photons, followed by a green tint that absorbs all red and blue photons—nothing would remain. To address this, let's assume our paints have 50% absorption instead:

tint_r50 = diag(1, 0.5, 0.5) 0000000000000000
tint_g50 = diag(0.5, 1, 0.5) 0000000000000000
tint_b50 = diag(0.5, 0.5, 1) 0000000000000000

Then:

output = tint_r50 * tint_g50 * source
       = diag(1, 0.5, 0.5) * diag(0.5, 1, 0.5) * source
       = 0.5 * u_r + 0.5 * u_g + 0.25 * u_b

This result is somewhat yellow 0000000000000000, but it’s not very bright, retaining only 41% of the light source’s energy. While all hues on the color wheel can technically be achieved by mixing red, green, and blue paints, the results tend to have low saturation. In other words, the colors appear dull, and the painting may not look vibrant. On closer inspection, the issue lies in the red, green, and blue tints absorbing too much light, as they suppress the other two types of photons. What if we used paints that absorb only one type of photon? Would that produce more saturated colors?

Cyan, Magenta, Yellow (CMY)

When red, green, or blue light is absorbed, the resulting colors are cyan, magenta, and yellow, respectively:

tint_c50 = diag(0.5, 1, 1) 0000000000000000
tint_m50 = diag(1, 0.5, 1) 0000000000000000
tint_y50 = diag(1, 1, 0.5) 0000000000000000

-> 0000000000000000

Let’s attempt to create red by mixing 50% magenta with 50% yellow:

output = tint_m50 * tint_y50 * source
       = diag(1, 0.5, 1) * diag(1, 1, 0.5) * source
       = 1 * u_r + 0.5 * u_g + 0.5 * u_b

The result is a red tint at 50% 0000000000000000, retaining 67% of the light source's energy. While it still appears somewhat dull, we can achieve greater saturation by using purer forms of magenta and yellow:

tint_c75 = diag(0.25, 1, 1) 0000000000000000
tint_m75 = diag(1, 0.25, 1) 0000000000000000
tint_y75 = diag(1, 1, 0.25) 0000000000000000

Then:

output = tint_m75 * tint_y75 * source
       = diag(1, 0.25, 1) * diag(1, 1, 0.25) * source
       = 1 * u_r + 0.25 * u_g + 0.25 * u_b

The result is a red tint at 75% 0000000000000000, retaining 50% of the light source's energy with noticeably higher saturation.

Conclusion

LCD monitors excel at producing vibrant colors because the light source is divided into three channels, each capable of creating highly saturated RGB colors that directly stimulate the human photoreceptors. By splitting the white backlight and tinting each channel to isolate the desired color while absorbing the other two, the process inherently loses two-thirds of the light's energy before it leaves the display. However, this energy loss is easily compensated with a sufficiently bright backlight—when was the last time you used your monitor at full brightness?

In painting, the light source is reflected off the canvas and passes through all the paint pigments sequentially. This creates a reductive compounding effect, where each pigment absorbs specific photons from the total light, progressively reducing the available spectrum. Mixing colors with RGB pigments is possible, but the mixtures darken quickly because each pigment absorbs two types of photons, which is overly restrictive. In contrast, CMY (cyan, magenta, yellow) is a much better choice for primary colors, as each pigment absorbs only one type of photon, allowing for more saturated color mixtures. As demonstrated in the experiment above, CMY can be combined to recreate RGB colors with the same saturation. In contrast, attempting to mix RGB to achieve CMY results in darker, less vibrant colors.

Although there are other options for primary colors, CMY is the most widely used, particularly in digital printers, which often employ a CMYK palette (with K representing black for controlling brightness). The traditional red, yellow, and blue palette used in painting is likely a version of the magenta, yellow, and cyan palette, with less emphasis placed on precise naming.