Blending modes

Normal

The source is composited over the destination and the result replaces the destination.
res.rgb = top.rgb + btm.rgb * (1.0 - top.a);
res.a = top.a + btm.a * (1.0 - top.a); 
                

Darken

The resultant color is the darker of source or destination colors. If the source is darker, it replaces the destination. Otherwise, the destination is preserved.
res.rgb = min(top.rgb * btm.a, btm.rgb * top.a) +
              top.rgb * (1.0 - btm.a) + btm.rgb * (1.0 - top.a);
res.a = top.a + btm.a * (1.0 - top.a); 
                

Lighten

The resultant color is the lighter of source or destination colors. If the source is lighter, it replaces the destination. Otherwise, the destination is preserved.
res.rgb = max(top.rgb * btm.a, btm.rgb * top.a) +
              top.rgb * (1.0 - btm.a) + btm.rgb * (1.0 - top.a);
res.a = top.a + btm.a * (1.0 - top.a);
                

Multiply

The source color is multiplied by the destination color and replaces the destination. The resultant color is always at least as dark as either the source or destination color. Multiplying any color with black results in black. Multiplying any color with white preserves the original color.
res.rgb = top.rgb * btm.rgb + top.rgb * (1.0 - btm.a) + btm.rgb * (1.0 - top.a);
res.a = top.a + btm.a * (1.0 - top.a);
                

Screen

The source and destination colors are complemented, multiplied and the resultant color replaces the destination. The resultant color is always at least as light as either the source or destination color. Screening any color with white results in white. Screening any color with black preserves the original color.
res.rgb = top.rgb + btm.rgb - top.rgb * btm.rgb;
res.a = top.a + btm.a * (1.0 - top.a);
                

Colour burn

Colour burn

The destination color is darkened to reflect the source color. Painting with white preserves the original color.
res.rgb = top.a * btm.a * (1.0 - min(top.a * (btm.a - btm.rgb) / 
                                    (top.rgb * (btm.a + 0.000001)), 1.0)) + 
          top.rgb * (1.0 - btm.a) + btm.rgb * (1.0 - top.a);
res.a = top.a + btm.a * (1.0 - top.a);
                

Linear burn

Linear burn

Sums the value in the two layers and subtracts 1. This is the same as inverting each layer, adding them together (as in Linear Dodge), and then inverting the result. Blending with white leaves the image unchanged.
res.rgb = top.rgb * btm.a + btm.rgb * top.a - btm.a * top.a + 
          top.rgb * (1.0 - btm.a) + btm.rgb * (1.0 - top.a);
res.a = top.a + btm.a * (1.0 - top.a);
                

Colour dodge

Colour dodge

The destination color is brightened to reflect the source color. Painting with black preserves the original color.
res.rgb = top.a * btm.a * min(btm.rgb * top.a / ((top.a - top.rgb) * 
                             (btm.a + 0.000001)),1.0) + 
          top.rgb * (1.0 - btm.a) + btm.rgb * (1.0 - top.a);
res.a = top.a + btm.a * (1.0 - top.a);
                

Linear dodge

Simply sums the values in the two layers (also known as additive blending. Blending with white gives white. Blending with black does not change the image. When top layer contains a homogeneous color, this effect is equivalent to changing the output black point to this color, and (input) white point to the inverted color. The contrast is decreased when there is no clipping.
res.rgb = top.rgb * btm.a + btm.rgb * top.a + 
          top.rgb * (1.0 - btm.a) + btm.rgb * (1.0 - top.a);
res.a = top.a + btm.a * (1.0 - top.a);
                

Overlay

The destination color is used to determine if the resultant is either a multiplication or screening of the colors. Source colors overlay the destination whilst preserving its highlights and shadows. The destination color is mixed with the source color to reflect the destination lightness or darkness.
vec3 btm_f = vec3(float(btm.r * 2.0 > btm.a),
                  float(btm.g * 2.0 > btm.a),
                  float(btm.b * 2.0 > btm.a));
res.rgb = (top.rgb * (1.0 + btm.a) + btm.rgb * (1.0 + top.a) - 
           2.0 * btm.rgb * top.rgb - btm.a * top.a) * btm_f + 
          (2.0 * top.rgb * btm.rgb + top.rgb * (1.0 - btm.a) + 
           btm.rgb * (1.0 - top.a)) * (1.0 - btm_f);
res.a = top.a + btm.a * (1.0 - top.a);
                

Soft light

The source color is used to determine if the resultant color is darkened or lightened. If the source color is lighter than 0.5, the destination is lightened. If the source color is darker than 0.5, the destination is darkened, as if it were burned in. The degree of darkening or lightening is proportional to the difference between the source color and 0.5. If it is equal to 0.5, the destination is unchanged. Painting with pure black or white produces a distinctly darker or lighter area, but does not result in pure black or white.
vec3 m = btm.rgb / (btm.a + 0.000001);
vec3 a = 2.0 * top.rgb - top.a;
vec3 b = btm.a * a;
vec3 c = top.rgb - top.rgb * btm.a + btm.rgb;
vec3 d_src = 2.0 * top.rgb;
vec3 q_dst = 4.0 * btm.rgb;
vec3 eq1 = vec3(float(d_top.r <= top.a),
                float(d_top.g <= top.a),
                float(d_top.b <= top.a));
vec3 eq2 = vec3(float(q_btm.r <= btm.a),
                float(q_btm.g <= btm.a),
                float(q_btm.b <= btm.a));
vec3 d = eq2 * (16.0 * m * m * m - 12.0 * m * m + 3.0 * m) + 
         (1.0 - eq2) * (sqrt(m) - m);
vec3 eq1val = btm.rgb * (top.a + a * (1.0 - m)) + 
              top.rgb * (1.0 - btm.a) + btm.rgb * (1.0 - top.a);
vec3 eq2val = b * d + c;
res.rgb = eq1 * eq1val + (1.0 - eq1) * eq2val;
res.a = top.a + btm.a * (1.0 - top.a);
                

Hard light

The source color is used to determine if the resultant is either a multiplication or screening of the colors. If the source color is lighter than 0.5, the destination is lightened as if it were screened. If the source color is darker than 0.5, the destination is darkened, as if it were multiplied. The degree of lightening or darkening is proportional to the difference between the source color and 0.5. If it is equal to 0.5 the destination is unchanged. Painting with pure black or white produces black or white.
vec3 btm_f = vec3(float(top.r * 2.0 > top.a),
                  float(top.g * 2.0 > top.a),
                  float(top.b * 2.0 > top.a));
res.rgb = (top.rgb * (1.0 + btm.a) + btm.rgb * (1.0 + top.a) - 
           2.0 * btm.rgb * top.rgb - btm.a * top.a) * btm_f + 
          (2.0 * top.rgb * btm.rgb + top.rgb * (1.0 - btm.a) + 
           btm.rgb * (1.0 - top.a)) * (1.0 - btm_f);
res.a = top.a + btm.a * (1.0 - top.a);
                

Vivid light

Combines Color Dodge and Color Burn (rescaled so that neutral colors become middle gray). Dodge applies when values in the source are lighter than middle gray, and burn to darker values. The middle gray is the neutral color. When color is lighter than this, this effectively moves the white point of the bottom layer down by twice the difference; when it is darker, the black point is moved up by twice the difference. The perceived contrast increases.
vec3 btm_f = vec3(float(top.r * 2.0 > top.a),
                  float(top.g * 2.0 > top.a),
                  float(top.b * 2.0 > top.a));
res.rgb = (btm_f * clamp(btm.rgb * top.a / 
                         (2.0 * (top.a - top.rgb) * (btm.a + 0.000001)), 0.0, 1.0) + 
           (1.0 - btm_f) * clamp((2.0 * btm.a * top.rgb - (btm.a - btm.rgb) * top.a) / 
                                 (2.0 * top.rgb * (btm.a + 0.000001)), 0.0, 1.0)) * 
          top.a * btm.a + top.rgb * (1.0 - btm.a) + btm.rgb * (1.0 - top.a);
res.a = top.a + btm.a * (1.0 - top.a);
                

Linear light

Combines Linear Dodge and Linear Burn (rescaled so that neutral colors become middle gray). Dodge applies to values of source lighter than middle gray, and burn to darker values. The calculation simplifies to the sum of bottom layer and twice the top layer, subtract 1. The contrast decreases.
res.rgb = btm.rgb * top.a + 2.0 * top.rgb * btm.a - top.a * btm.a + 
          top.rgb * (1.0 - btm.a) + btm.rgb * (1.0 - top.a);
res.a = top.a + btm.a * (1.0 - top.a);
                

Pin light

Replaces the colors, depending on the source. If the source (light source) is lighter than 50% gray, pixels darker than the source are replaced, and pixels lighter than the source do not change. If the source is darker than 50% gray, pixels lighter than the blend color are replaced, and pixels darker than the blend color do not change. This is useful for adding special effects to an image.
vec3 btm_f = vec3(float(top.r * 2.0 > top.a),
                  float(top.g * 2.0 > top.a),
                  float(top.b * 2.0 > top.a));
res.rgb = btm_f * clamp(max(btm.rgb * top.a, 
                            2.0 * top.rgb * btm.a - top.a * btm.a), 0.0, 1.0) + 
          (1.0 - btm_f) * clamp(min(btm.rgb * top.a, 2.0 * top.rgb * btm.a), 0.0, 1.0) +
          top.rgb * (1.0 - btm.a) + btm.rgb * (1.0 - top.a);
res.a = top.a + btm.a * (1.0 - top.a);
                

Difference

The resultant color is the absolute difference between the source and destination colors. The destination color is inverted when white is used. The destination color is preserved when black is used.
res.rgb = top.rgb + btm.rgb - 2.0 * min(top.rgb * btm.a, btm.rgb * top.a);
res.a = top.a + btm.a * (1.0 - top.a);
                

Exclusion

The resultant color is similar to that of the difference operation. However, the exclusion resultant color appears as a lower contrast than that of the difference resultant color. The destination color is inverted when white is used. The destination color is preserved when black is used.
res.rgb = (top.rgb * btm.a + btm.rgb * top.a - 2.0 * top.rgb * btm.rgb) + 
           top.rgb * (1.0 - btm.a) + btm.rgb * (1.0 - top.a);
res.a = top.a + btm.a * (1.0 - top.a);
                

Lighter colour

Compares the total of all channel values for the source and destination and displays the higher value color. Lighter Color does not produce a third color, which can result from the Lighten blend, because it chooses the highest channel values from both the destination and source to create the result color.
float top_f = float((0.2126 * top.r + 0.7152 * top.g + 0.0722 * top.b) >
                    (0.2126 * btm.r + 0.7152 * btm.g + 0.0722 * btm.b));
res.rgb = top_f * top.rgb * btm.a + (1.0 - top_f) * btm.rgb * top.a + 
          top.rgb * (1.0 - btm.a) + btm.rgb * (1.0 - top.a);
res.a = top.a + btm.a * (1.0 - top.a);
                

Darker colour

Compares the total of all channel values for the source and destination and displays the lower value color. Darker Color does not produce a third color, which can result from the Darken blend, because it chooses the lowest channel values from both the source and the destiantion to create the result color.
float top_f = float((0.2126 * top.r + 0.7152 * top.g + 0.0722 * top.b) <
                    (0.2126 * btm.r + 0.7152 * btm.g + 0.0722 * btm.b));
res.rgb = top_f * top.rgb * btm.a + (1.0 - top_f) * btm.rgb * top.a + 
          top.rgb * (1.0 - btm.a) + btm.rgb * (1.0 - top.a);
res.a = top.a + btm.a * (1.0 - top.a);
                

Luminosity

Preserves the hue and chroma of the destination, while adopting the luma of the source.
vec3 top_rgb = top.rgb / (top.a + 0.000001);
vec3 btm_rgb = btm.rgb / (btm.a + 0.000001);
vec3  res_rgb = btm_rgb + dot(vec3(0.3, 0.59, 0.11), top_rgb - btm_rgb);
float l = dot(vec3(0.3, 0.59, 0.11), res_rgb);
float n = min(min(res_rgb.r, res_rgb.g), res_rgb.b);
float x = max(max(res_rgb.r, res_rgb.g), res_rgb.b);
if (n < 0.0) {
    res_rgb = l + (res_rgb - l) * l / (l - n);
}
if (x > 1.0) {
    res_rgb = l + (res_rgb - l) * (1.0 - l) / (x - l);
}
res.rgb = (1.0 - top.a) * btm.rgb + 
          (1.0 - btm.a) * top.rgb + 
          top.a * btm.a * res_rgb;
res.a = top.a + btm.a * (1.0 - top.a);
                

Saturate

Preserves the luma and hue of the destination, while adopting the chroma of the source.
vec3 top_rgb = top.rgb / (top.a + 0.000001);
vec3 btm_rgb = btm.rgb / (btm.a + 0.000001);
float s = max(max(top_rgb.r, top_rgb.g), top_rgb.b) - 
          min(min(top_rgb.r, top_rgb.g), top_rgb.b);
float l_dst = dot(vec3(0.3, 0.59, 0.11), btm_rgb);
bvec3 clr_sort = greaterThan(btm_rgb.rgb, btm_rgb.brg);
btm_rgb = mix(btm_rgb, vec3(btm_rgb.y, btm_rgb.xz), step(btm_rgb.x, btm_rgb.y));
btm_rgb = mix(btm_rgb, vec3(btm_rgb.x, btm_rgb.zy), step(btm_rgb.y, btm_rgb.z));
btm_rgb = mix(btm_rgb, vec3(btm_rgb.y, btm_rgb.xz), step(btm_rgb.x, btm_rgb.y));
btm_rgb = (btm_rgb.x == btm_rgb.z ?
           vec3(0.0) :
           vec3(s, (btm_rgb.y - btm_rgb.z) * s / (btm_rgb.x - btm_rgb.z), 0.0));
btm_rgb = mix(vec3(btm_rgb.z, btm_rgb.yx), btm_rgb, float(clr_sort.x));
btm_rgb = mix(btm_rgb, vec3(btm_rgb.y, btm_rgb.xz), float(clr_sort.y == clr_sort.x));
btm_rgb = mix(btm_rgb, vec3(btm_rgb.x, btm_rgb.zy), float(clr_sort.z == clr_sort.x));
vec3  res_rgb = btm_rgb + l_dst - dot(vec3(0.3, 0.59, 0.11), btm_rgb);
float l = dot(vec3(0.3, 0.59, 0.11), res_rgb);
float n = min(min(res_rgb.r, res_rgb.g), res_rgb.b);
float x = max(max(res_rgb.r, res_rgb.g), res_rgb.b);
if (n < 0.0) {
    res_rgb = l + (res_rgb - l) * l / (l - n); 
}
if (x > 1.0) {
    res_rgb = l + (res_rgb - l) * (1.0 - l) / (x - l);
}
res.rgb = (1.0 - top.a) * btm.rgb + 
          (1.0 - btm.a) * top.rgb + 
          top.a * btm.a * res_rgb;
res.a = top.a + btm.a * (1.0 - top.a);
                

Hue

Preserves the luma and chroma of the destination, while adopting the hue of the source.
vec3 top_rgb = top.rgb / (top.a + 0.000001);
vec3 btm_rgb = btm.rgb / (btm.a + 0.000001);
float s = max(max(btm_rgb.r, btm_rgb.g), btm_rgb.b) - 
          min(min(btm_rgb.r, btm_rgb.g), btm_rgb.b);
float l_dst = dot(vec3(0.3, 0.59, 0.11), btm_rgb);
bvec3 clr_sort = greaterThan(top_rgb.rgb, top_rgb.brg);
top_rgb = mix(top_rgb, vec3(top_rgb.y, top_rgb.xz), step(top_rgb.x, top_rgb.y));
top_rgb = mix(top_rgb, vec3(top_rgb.x, top_rgb.zy), step(top_rgb.y, top_rgb.z));
top_rgb = mix(top_rgb, vec3(top_rgb.y, top_rgb.xz), step(top_rgb.x, top_rgb.y));
top_rgb = (top_rgb.x == top_rgb.z ?
           vec3(0.0) :
           vec3(s, (top_rgb.y - top_rgb.z) * s / (top_rgb.x - top_rgb.z), 0.0));
top_rgb = mix(vec3(top_rgb.z, top_rgb.yx), top_rgb, float(clr_sort.x));
top_rgb = mix(top_rgb, vec3(top_rgb.y, top_rgb.xz), float(clr_sort.y == clr_sort.x));
top_rgb = mix(top_rgb, vec3(top_rgb.x, top_rgb.zy), float(clr_sort.z == clr_sort.x));
vec3  res_rgb = top_rgb + l_dst - dot(vec3(0.3, 0.59, 0.11), top_rgb);
float l = dot(vec3(0.3, 0.59, 0.11), res_rgb);
float n = min(min(res_rgb.r, res_rgb.g), res_rgb.b);
float x = max(max(res_rgb.r, res_rgb.g), res_rgb.b);
if (n < 0.0) {
    res_rgb = l + (res_rgb - l) * l / (l - n); 
}
if (x > 1.0) {
    res_rgb = l + (res_rgb - l) * (1.0 - l) / (x - l);
}
res.rgb = (1.0 - top.a) * btm.rgb +
          (1.0 - btm.a) * top.rgb + 
          top.a * btm.a * res_rgb;
res.a = top.a + btm.a * (1.0 - top.a);
                

Colour

Preserves the luma of the destination, while adopting the hue and chroma of the source.
vec3 top_rgb = top.rgb / (top.a + 0.000001);
vec3 btm_rgb = btm.rgb / (btm.a + 0.000001);
vec3  res_rgb = top_rgb + dot(vec3(0.3, 0.59, 0.11), btm_rgb - top_rgb);
float l = dot(vec3(0.3, 0.59, 0.11), res_rgb);
float n = min(min(res_rgb.r, res_rgb.g), res_rgb.b);
float x = max(max(res_rgb.r, res_rgb.g), res_rgb.b);
if (n < 0.0) {
    res_rgb = l + (res_rgb - l) * l / (l - n); 
}
if (x > 1.0) {
    res_rgb = l + (res_rgb - l) * (1.0 - l) / (x - l);
}
res.rgb = ((1.0 - top.a) * btm.rgb) + 
          ((1.0 - btm.a) * top.rgb) + 
          (top.a * btm.a * res_rgb);
res.a = top.a + btm.a * (1.0 - top.a);
                

Divide

Simply divides pixel values of one layer with the other, but it's useful for brightening photos if the color is on grey or less. It is also useful for removing a color tint from a photo. If you create a layer that is the color of the tint you wish to remove - such as a pale blue, for scenes that are too cool in color temperature - Divide mode will return that color to white in the resulting composite, as any value divided by itself equals 1.0 (white).
vec3 top_rgb = top.rgb / (top.a + 0.000001);
vec3 btm_rgb = btm.rgb / (btm.a + 0.000001);
vec3 res_rgb = clamp(btm_rgb / top_rgb, 0.0, 1.0);
res.rgb = (1.0 - top.a) * btm.rgb + 
          (1.0 - btm.a) * top.rgb + 
          top.a * btm.a * res_rgb;
res.a = top.a + btm.a * (1.0 - top.a);
                

Subtract

Sums the value in the two layers and subtracts 1. Unlike Linear Burn, blending with white affects the image.
vec3 top_rgb = top.rgb / (top.a + 0.000001);
vec3 btm_rgb = btm.rgb / (btm.a + 0.000001);
vec3 res_rgb = clamp(btm_rgb - top_rgb, 0.0, 1.0);
res.rgb = ((1.0 - top.a) * btm.rgb) + 
          ((1.0 - btm.a) * top.rgb) + 
          (top.a * btm.a * res_rgb);
res.a = top.a + btm.a * (1.0 - top.a);
                

Dissolve

The Dissolve blend mode on acts on transparent and partially transparent pixels – it treats transparency as a pixel pattern and applies a diffusion dither pattern.
vec2 xy = (gl_FragCoord.xy + 1.0) * 0.5;
float dy = fract(sin(dot(xy, vec2(75.542, 35.7124))) * 22854.812);
xy.y += dy;
float randV = fract(sin(dot(xy, vec2(2.124, 24.3598))) * 22854.812);
float clrA = top.a;
top.rgb /= (top.a + 0.000001);
top.a = step(0.0000001, top.a);
top.rgb *= top.a;
res.rgb = top.a == 0.0 ? btm.rgb : (randV < clrA ? top.rgb : btm.rgb);
res.a = top.a == 0.0 ? btm.a : (randV < clrA ? top.a : btm.a);
                

Hardmix

Adds the red, green and blue channel values of the source to the RGB values of the destination. If the resulting sum for a channel is 1 or greater, it receives a value of 1; if less than 1, a value of 0. Therefore, all blended pixels have red, green, and blue channel values of either 0 or 1. This changes all pixels to primary additive colors (red, green, or blue), white, or black.
vec3 top_rgb = top.rgb / (top.a + 0.000001);
vec3 btm_rgb = btm.rgb / (btm.a + 0.000001);
vec3 res_rgb = step(1.0, top_rgb + btm_rgb);
res.rgb = (1.0 - top.a) * btm.rgb + 
          (1.0 - btm.a) * top.rgb + 
          top.a * btm.a * res_rgb;
res.a = top.a + btm.a * (1.0 - top.a);
                

Behind

The destination is composited over the source and the result replaces the destination.
res.rgb = btm.rgb + top.rgb * (1.0 - btm.a);
res.a = top.a + btm.a * (1.0 - top.a);
                

References

  1. Scalable Vector Graphics (SVG) Compositing Specification.
  2. Compositing and Blending Level 1
  3. Photoshop Blend Modes Explained
  4. Blending mode descriptions