Blend Modes

This is part of the WebGL image processing series and it relies on information in previous articles. See all articles here.

It is designed to be used on desktop.

In this article we’ll look at how to implement Photoshop-like Blend Modes with WebGL. Many of these algorithms come from the glsl-blend package which is licensed under the MIT license.

What are Blend Modes

Blend Modes are algorithms that blend two color values. We take a base color, add a blend color and output a result color.

Blend Modes were introduced with Photoshop 3.0 back in 1994. We’ll cover a subset of these and how to create them in GLSL.

Neutral Colors

Blend Modes have Neutral Colors that have no effect when blended, we can group Blend Modes by their Neutral Colors:

  • Normal. Does not have a Neutral Color.
  • Darken. White is the Neutral Color and will become transparent.
  • Lighten. Black is the Neutral Color and will become transparent.
  • Contrast. 50% gray is the Neutral Color and will become transparent.

Normal Blend Modes

Normal

Normal is the default Blending Mode for Photoshop layers and does not have an algorithm that blends the color data. The result color will always be the blend color.

vec4 blendNormal(vec4 base, vec4 blend){
  return blend;
}

Darken Blend Modes

These Blend Modes make the Result Color darker. White is the Neutral Color for this category and will become transparent.

Darken

Darken compares the base and blend color and outputs the darker value as its result color. This is the opposite of the Lighten Blend Mode we’ll cover later.

vec4 blendDarken(vec4 base, vec4 blend){
  return vec4(
    min(base.r, blend.r),
    min(base.g, blend.g),
    min(base.b, blend.b),
    1.0
  );
}

Multiply

The multiply Blend Mode, multiplies the base and blends colors, resulting in a darker color. Multiplying any color by black results in black, while white will have no effect. This is the opposite of the Screen Blend Mode we’ll cover later.

vec4 blendMultiply(vec4 base, vec4 blend){
  return vec4(
    base.r * blend.r,
    base.g * blend.g,
    base.b * blend.b,
    1.0
  );
}

Color Burn

Color Burn darkens the base color to reflect the blend color by increasing the contrast between the two. This is the opposite of the Color Dodge Blend Mode we’ll cover later.

float blendColorBurn(float base, float blend){
  return (blend==0.0)?blend:max((1.0-((1.0-base)/blend)),0.0);
}

vec4 blendColorBurn(vec4 base, vec4 blend){
  return vec4(
    blendColorBurn(base.r, blend.r),
    blendColorBurn(base.g, blend.g),
    blendColorBurn(base.b, blend.b),
    1.0
  );
}

Lighten Blend Modes

These Blend Modes make the Result Color lighter, and are generally complementary to the Darken Blend Modes. Black is the Neutral Color for this category and will become transparent.

Lighten

Lighten compares the base and blend color and outputs the lighter value as its result color. This is the opposite of the Darken Blend Mode we covered previously.

vec4 blendLighten(vec4 base, vec4 blend){
  return vec4(
    max(base.r, blend.r),
    max(base.g, blend.g),
    max(base.b, blend.b),
    1.0
  );
}

Screen

The Screen Blend Mode, multiplies the inverse of the base and blend colors, resulting in a lighter color. Screening any color by white results in white, while black will have no effect. This is the opposite of the Multiply Blend Mode we covered previously.

vec4 blendScreen(vec4 base, vec4 blend){
  return vec4(
    1.0 - ((1.0 - base.r) * (1.0 - blend.r)),
    1.0 - ((1.0 - base.g) * (1.0 - blend.g)),
    1.0 - ((1.0 - base.b) * (1.0 - blend.b)),
    1.0
  );
}

Color Dodge

Color Doge brightens the base color to reflect the blend color by decreasing contrast between the two. This is the opposite of the Color Burn Blend Mode we covered previously.

float blendColorDodge(float base, float blend){
  return (blend == 1.0) ? blend : min(base / (1.0 - blend), 1.0);
}

vec4 blendColorDodge(vec4 base, vec4 blend){
  return vec4(
    blendColorDodge(base.r, blend.r),
    blendColorDodge(base.g, blend.g),
    blendColorDodge(base.b, blend.b),
    1.0
  );
}

Contrast Blend Modes

Contrast Blend Modes build on the Blend Modes we just covered. Rather than always darkening or lightening the color, they will darkening if the base color is darker than 50% gray or lighter if it’s lighter than 50% gray.

Overlay

Overlay builds on the Screen and Multiply Blend Modes. It uses Screen on color brighter than 50% gray and Multiply on colors darker than 50% gray.

float blendOverlay(float base, float blend){
  return base < 0.5 ? (2.0*base*blend):(1.0-2.0*(1.0-base)*(1.0-blend));
}

vec4 blendOverlay(vec4 base, vec4 blend){
  return vec4(
    blendOverlay(base.r, blend.r),
    blendOverlay(base.g, blend.g),
    blendOverlay(base.b, blend.b),
    1.0
  );
}

Soft Light

Soft Light darkens or lightens the colors, depending on the blend color. The effect mimics shining a diffused light on the image. If the blend color is lighter than 50% gray, the image is lightened. If the blend color is darker than 50% gray, the image is darkened.

float blendSoftLight(float base, float blend){
  return (blend<0.5)?(2.0*base*blend+base*base*(1.0-2.0*blend)):(sqrt(base)*(2.0*blend-1.0)+2.0*base*(1.0-blend));
}

vec4 blendSoftLight(vec4 base, vec4 blend){
  return vec4(
    blendSoftLight(base.r, blend.r),
    blendSoftLight(base.g, blend.g),
    blendSoftLight(base.b, blend.b),
    1.0
  );
}

Hard Light

Hard Light darkens or lightens the colors, depending on the blend color.The effect mimics shining a harsh light on the image. If the blend color is darker than 50% gray, the image is darkened.

float blendHardLight(float base, float blend){
  return base < 0.5 ? (2.0*base*blend):(1.0-2.0*(1.0-base)*(1.0-blend));
}

vec4 blendHardLight(vec4 base, vec4 blend){
  return vec4(
    blendHardLight(base.r, blend.r),
    blendHardLight(base.g, blend.g),
    blendHardLight(base.b, blend.b),
    1.0
  );
}

In the next article, we'll look at how we can add an analog feel to our images by simulating film grain.

Feedback and suggestions

Have a suggestion or want to show me your work?
Get in touch at via email or Twitter @maximmcnair.