Blending and masking

Control how a layer mixes with the layers below it, and reveal one layer through another.

Blending a layer with the layers below

A layer's blend mode sets how its colors combine with everything drawn before it. It works like mix-blend-mode in CSS or the blend menu on a Figma layer. Every component takes a blendMode prop, and the default is normal: the layer sits on top, and only its transparent parts show what's below.

Here a pink circle uses multiply over a gradient:

<Shader>
  <LinearGradient />

  {/* Multiply darkens by multiplying colors */}
  <Circle color="#ff0088" radius={0.5} blendMode="multiply" />
</Shader>

There are 20 blend modes:

  • Basic: normal.
  • Darkening: multiply, darken, colorBurn, linearBurn.
  • Lightening: screen, lighten, colorDodge, linearDodge.
  • Contrast: overlay, softLight, hardLight.
  • Difference: difference, exclusion.
  • Color: hue, saturation, color, luminosity.
  • Color space: normal-oklab, normal-oklch. These work like normal but mix the colors in the OKLab or OKLCh color space instead of RGB.

Mixing several blend modes

Each layer has its own blend mode, applied against the result of every layer before it. Two overlapping circles, one with screen and one with difference:

<Shader>
  <LinearGradient />

  <Circle color="#00ffff" radius={0.6} center={{ x: 0.4, y: 0.5 }} blendMode="screen" />
  <Circle color="#ff00ff" radius={0.6} center={{ x: 0.6, y: 0.5 }} blendMode="difference" />
</Shader>

Where the circles overlap, the magenta circle takes the difference against the cyan one as well as the gradient.

Fading a layer with opacity

The opacity prop sets how strongly a layer shows, from 0 (fully transparent) to 1 (fully opaque):

<Shader>
  <LinearGradient />

  <Circle color="#ff0088" radius={0.8} opacity={0.5} />
</Shader>

Opacity multiplies the layer's alpha before it blends, so it works with every blend mode.

Hiding a layer

Every component has a visible prop, which defaults to true. Set it to false to take the component out of the output entirely:

<Circle visible={false} />

opacity={0} is different: the component still renders, then gets blended at zero strength. A hidden component still exists in the tree, so it can serve as a mask for another layer.

Masking with another layer

A mask decides where a layer shows, based on another layer's pixels. It works like CSS mask-image, except the mask is a live layer instead of an image.

Give the mask layer an id (a string), then set maskSource to that id on the layer you want to mask. Set the mask layer to visible={false} so it shapes the other layer without being drawn itself:

<Shader>
  {/* This circle acts as the mask */}
  <Circle id="myMask" radius={0.8} visible={false} />

  {/* This gradient is masked by the circle */}
  <LinearGradient maskSource="myMask" />
</Shader>

Layer order doesn't matter for masking: the mask can come before or after the layer that uses it.

Choosing what the mask reads

The maskType prop sets which part of the mask layer decides visibility, like CSS mask-mode:

  • alpha (default): the mask's alpha channel. Opaque pixels show the layer.
  • alphaInverted: the inverted alpha channel. Transparent pixels show the layer.
  • luminance: the mask's brightness. Bright pixels show the layer.
  • luminanceInverted: the inverted brightness. Dark pixels show the layer.

Here a hidden checkerboard masks a circle by brightness, so the circle shows on the white squares:

<Shader>
  <LinearGradient />

  {/* Hidden checkerboard mask controls visibility */}
  <Checkerboard
    id="mask"
    colorA="#ffffff"
    colorB="#111111"
    visible={false}
  />

  {/* Visible where mask is bright */}
  <Circle
    color="#ff0088"
    radius={0.8}
    maskSource="mask"
    maskType="luminance"
  />
</Shader>

Next steps