How SVG Luminance-Based Masking Works

Luminance-based masking in SVG uses the brightness and opacity values of graphic elements inside a <mask> element to determine the transparency of a target shape. Pure white areas in the mask leave the masked graphic completely opaque, pure black areas make it fully transparent, and shades of grey or colors create varying levels of semi-transparency. This article explains the underlying mathematics, the role of color and alpha channels, and how the browser calculates final visibility.

The Luminance Calculation Formula

When an SVG element references a <mask> element, the rendering engine converts the color of every pixel inside that mask into a single grayscale luminance value. SVG follows the standard ITU-R BT.709 color conversion formula to calculate perceived luminance (\(Y\)):

\[Y = 0.2126 \times R + 0.7152 \times G + 0.0722 \times B\]

In this calculation, the red (\(R\)), green (\(G\)), and blue (\(B\)) channels are normalized between 0.0 and 1.0. Because the human eye is most sensitive to green light and least sensitive to blue light, the green channel contributes the most significantly to the final opacity of the masked object.

Integrating the Alpha Channel

Luminance is not evaluated in isolation; it is multiplied by the opacity (alpha channel) of the mask element. The effective mask coefficient (\(M\)) applied to the target element’s alpha channel is calculated as:

\[M = Y \times A\]

Where \(A\) is the computed alpha value of the mask pixel. If a mask element has a fill of #ffffff (white) but an opacity="0.5", the effective luminance calculation results in an opacity multiplier of 0.5 for the masked target.

How Color Values Affect Opacity

Because of the weighted formula, different colors with the same numeric channel values produce vastly different levels of transparency:

Implementing a Luminance Mask in SVG

By default, an SVG <mask> element has its mask-type CSS property set to luminance. The mask is defined within a <defs> block and referenced via the mask attribute on the target shape:

<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <linearGradient id="fade" x1="0" y1="0" x2="1" y2="0">
      <stop offset="0%" stop-color="white" />
      <stop offset="100%" stop-color="black" />
    </linearGradient>
    <mask id="luminanceMask">
      <!-- The gradient dictates opacity from 100% to 0% -->
      <rect x="0" y="0" width="200" height="200" fill="url(#fade)" />
    </mask>
  </defs>

  <!-- This rectangle fades out horizontally based on the mask's luminance -->
  <rect x="0" y="0" width="200" height="200" fill="royalblue" mask="url(#luminanceMask)" />
</svg>

In this example, the linear gradient transitions from white to black, creating a smooth horizontal fade on the target rectangle without modifying the target element’s own fill or opacity attributes directly.