Recreating VSCOcam image filters with CSS blend modes

CSS - August 23, 2016

At Outfit we tend to use a lot of tricks and techniques to accomplish some pretty basic image treatments, that are extremely easy to recreate in Photoshop. Things like image brightening, adding a blend mode, saturating controls are the staple of the Photoshop domain but can be easily accomplished with CSS… Usually these involve a pretty heavy use of -webkit-filter , background-blend-mode and mix-blend-mode.

During an Outfit ‘hack day’ I wanted to see how far I could push these effects and tried to implement some of the photoshop filters that don’t have a corresponding -webkit-filter like levels for example, and ended up re-creating all of VSCO’s default image filters.

A little bit about blend modes (and why they are important)

I know what blend modes are John, why are they relevant?

1. Background blend mode

56.88% ◒ 11.28%

IE ✘  
Edge ✘  
Firefox ✘ 2+ ✔ 30+ 
Chrome ✘ 4+ ✔ 35+ ◒ 46+² ✔ 47+ 
Safari ✘ 3.1+ ◒ 7.1+¹ 
Opera ✘ 9+ ✔ 22+ ◒ 33+² ✔ 34+ 

As the name suggests it simply blends the different background styles together. Simple right?

You can blend your background-image with a background-color for a cool image treatment.

.blend-background-with-color {
  background-image: url('face.png');
  background-color: red;
  background-blend-mode: multiply;
}

or you can blend multiple background images together for a cool double exposure effect

.blend-images-together {
  background-image: url('person.png'), url('mountain.png');
  background-blend-mode: screen;
}

2. Mix blend mode

56.85% ◒ 10.75%

IE ✘  
Edge ✘  
Firefox ✘ 2+ ✔ 32+ 
Chrome ✘ 4+ ✘ 29+¹ ✔ 41+ 
Safari ✘ 3.1+ ◒ 7.1+² 
Opera ✘ 9+ ✔ 29+

Similar to background-blend-mode but not limited to background images, mix-blend-mode, defines how an element’s content should blend with its background.

For example, creating the same double exposure effect with text and an image, instead of two images

<div class="image"></div> 
<div class="text">Blending is rad</div>
.text { 
  mix-blend-mode: lighten;
}
Blending is rad


Why these are important.

When I first found out about background-blend-mode I stumbled across this codepen demo on this css tricks article which very cleverly used background-blend-mode: multiply; to blend together CYMK separations to form a complete image. (How offset printing works)

Hover over the demo below to see it working


This is the main technique behind implementing the ‘levels’ filter. (the missing filter from the CSS filter set)


So what exactly are levels?

Essentially ‘levels’ are the varying amounts of how much red, green and blue (RGB) or how much cyan, magenta, yellow and black (CMYK) the image contains. VSCO’s filters, are for the most part, just variations of RGB levels.

photoshop levels example

And how do we create this with blend modes?

Not only will we need to create the RGB separations with background-blend-mode , we will also have to blend them all back together to get the finished image using mix-blend-mode , after that is done, we can change the colour that a separation is blending with to change the level of that colour in our image, creating the filters.



Step 1. Creating the RGB separations

For the sake of the demo and every step having a preview, I’ve omitted some basic CSS rules like dimensions and positioning

Ok, first things first. Creating the three RGB separations.

To start this off, I’ll be creating the divs that will act as my RGB separations, these will just be three divs called R, G and B

<!-- RGB in reverse order so R is the 'top layer' -->
<div class="b"></div>
<div class="g"></div>
<div class="r"></div>

Each of these divs will need to have a corresponding background-color .

.r {  background-color: #FF0000 }
.g {  background-color: #00FF00 }
.b {  background-color: #0000FF }


Cool. now that the separations are there, lets actually add an image to separate, I’ll just do this by adding a background-image to a class called image .

<div class="image b"></div>
<div class="image g"></div>
<div class="image r"></div>
/* image class comes before r, g and b in the stylesheet
   so the background shorthand doesn't override the 
   background colors */

.image {
  background: url('face.png') center center no-repeat; 
  background-size: cover;
}

.r { /* ... */ }
.g { /* ... */ }
.b { /* ... */ }


Now all im going to do is blend this image with the background color that is being set by the .r .g and .b classes.

The blend mode I’ll use is screen. To replace black with the color of the separation.

With Screen blend mode the values of the pixels in the two layers are inverted, multiplied, and then inverted again. This yields the opposite effect to multiply. - https://en.wikipedia.org/wiki/Blend_modes#Screen

.image {
  background-blend-mode: screen;
  /* ... */
}


It’s pretty useless having these separations sit side by side, so let’s stack them all on top of each other, I’m going to do this by wrapping all of the divs in an image-wrapper class, and absolutely positioning the separations on top of each other

<div class="image-wrapper">
  <!-- r g and b -->
</div>
.image-wrapper {
  position: relative;
  height: 400px;
  width: 100%;
}

.image {
  position: absolute;
  top: 0; 
  right: 0;
  bottom: 0;
  left: 0;
  /* ... */
}

Because our separations will always fill the image-wrapper you can make the image-wrapper whatever dimensions you want, and the image will still be a-ok.



Step 2. Blending those separations together to form a whole image.

So by this time I take it you are pretty sick of that obnoxious red split, so lets blend them together to get a whole image so we can start on creating the ‘filters’.

All this step is is adding a mix-blend-mode to the image class

.image {
  /* ... */ 
  mix-blend-mode: multiply;
}


As you’ve probably noticed the image is a little heavy on the contrast, we can fix this and make it a little closer to the original by simply adding some css filters to the image-wrapper

.image-wrapper {
  /* ... */ 
  filter: saturate(0.8) brightness(1.5) contrast(0.8);
}


Step 3. Creating the filters.

The next step is probably the easiest and at the same time, the hardest one. All I do to achieve this is add a class on the body ( or any element the image-wrapper is inside. ) with the name of my filter, and change the background-color of each of the separations are blending with and whatever filters are being applied, until the finished product looks like it does in VSCOcam.

This isn’t by any means an exact science as you’ll have to do it all by eye, but you can end up with a result that looks pretty damn close with pure css.

P5 Filter in VSCO

vsco filter screenshots

P5 Filter recreated in CSS

<body class="p5">
.p5 .image-wrapper { filter: saturate(0.8) contrast(1.2) brightness(1.2); }
.p5 .b { background-color: rgb(76, 54, 255); }
.p5 .g { background-color: rgb(73, 250, 79); }
.p5 .r { background-color: rgb(193, 96, 122);}


There you have it, you can even crack open the web inspector and poke around in any of the previews throughout the post if you don’t believe its only css.

I’m still not sure if this has any practical use outside of an Outfit template other than being a cool concept and an exercise to flex your creative CSS writing muscles.

Tweet me at @johnm__ if you actually found a practical use for it, or just found it interesting, because I’d love to hear it.