SVG effects for Instagram-like photo editing adjustments<!-- --> | <!-- -->IORoot
logo
IORoot

šŸ‘ØšŸ»ā€šŸŽØ SVG effects for Instagram-like photo editing adjustments

Note, this article is going to try to be as simple as possible. I'm not going into the depths of SVG functions and elements... more just a reference on how to do specific things. It's a cobbled collection of information I've found from other people rather than any critical-thinking on my part. Check out the links for more in-depth info on each topic.

bigben

Just some background info...

There you are, developing away on your latest project and CSS is kicking ass. It's allowing you to do all of those cool blend-modes and effects but you want to combine it with some SVG goodness.

This was what I was trying to achieve on my latest project for my company websiteĀ LondonParkour.com

I essentially built aĀ WordPress pluginĀ (It's super alpha stage - don't judge me) to build SVGs with a post image as a base. I could then layer effects and vector shapes over the top of the image and do whatever I liked with it. This gave me the flexibility to apply, in bulk, to any group of posts I wanted.

The thinking was to have some generative-art part to it which creates random shapes and what-not, (you can see the results on the londonparkour.comĀ article section), but I'm digressing...

What I also wanted to achieve was Instagram-like adjustments to the images - in SVG. So that's what this article is all about - how to get those effects. I'll link to all of the posts I used to help me figure this all out too, but essentially, this is how to do the basics of image adjustments on bitmap images using SVG filters and shapes.

Main Links of where I found this information are:

Big thanks to all of these people and their efforts. I've just copied their hard work and tweaked it a little to do what I needed, these are the real innovators who figured out how the filters worked.

šŸŽ› The Adjustments

šŸ The beginning

So, we need an image to start with. I'm going to use this one I took of Big Ben.

Our base SVG will be this:

<svg viewBox="0 0 1500 1000">
  <image xlink:href="bigben.jpg" width="1500" height="1000"></image>
</svg>

I'm not going to bother with theĀ xmlns="http://www.w3.org/2000/svg"Ā and theĀ xmlns:xlink="http://www.w3.org/1999/xlink"Ā for now. I want to make this all as easy to read as possible.

Which gives us this:

Alright, let's get our mitts dirty. We need to define (as in, use theĀ <def></def>Ā tags) some filters to use on the image. So let's start with:


šŸŽØ Saturation

This is an easy one to get going with. The magic of saturation is this code:

<feColorMatrix 
    type="saturate" 
    in="SourceGraphic" 
    values="3"
 />

Saturation Filter

Now, to use this code, we need to define a filter using theĀ <filter id="overSaturateThis"></filter>Ā tags with an ID to use. We then tell the image to use this filter. Pretty simple.

The input is theĀ sourceGraphicĀ (the image) and we're just using a 'saturate' on the image. The value can be changed up or down from 1. 1 being no change. 3 is hugely oversaturated.

So the code becomes:

<svg viewBox="0 0 1500 1000">
    <defs>
        <filter id="overSaturateThis">
            <feColorMatrix 
                type="saturate" 
                in="SourceGraphic" 
                values="3"
            ></feColorMatrix>
        </filter>
    </defs>
    
  <image xlink:href="bigben.jpg" width="1500" height="1000" filter="url(#overSaturateThis)"></image>
</svg>

šŸŒž Brightness

Brightness is slightly more complex, but not much. Essentially, we can fiddle with the Red, Green & Blue elements of the pixels to increase or decrease the brightness.

<feComponentTransfer in="sourceGraphic">
    <feFuncR type="linear" slope="0.5"/>
    <feFuncG type="linear" slope="0.5"/>
    <feFuncB type="linear" slope="0.5"/>
</feComponentTransfer>

Brightness filter

You can see theĀ feFuncRĀ controls the Red,Ā feFuncGĀ the green andĀ feFuncBĀ the blue. TheĀ slopeĀ allows us to increase or decrease the brightness by giving a number lower or higher than 1.

Let's bring the brightness down by half (0.5) in this example.

<svg viewBox="0 0 1500 1000">
    <defs>
        <filter id="lowerTheBrightness">
            <feComponentTransfer in="sourceGraphic">
                <feFuncR type="linear" slope="0.5"/>
                <feFuncG type="linear" slope="0.5"/>
                <feFuncB type="linear" slope="0.5"/>
            </feComponentTransfer>
        </filter>
    </defs>
    
  <image xlink:href="bigben.jpg" width="1500" height="1000" filter="url(#lowerTheBrightness)"></image>
</svg>

ā¬›ļøā¬œļø Contrast

Alright, this one requires a little maths... Nothing difficult though.

Start with the amount of contrast you want to add / remove from the picture... let's say increase contrast toĀ 1.2

Half the value :Ā 0.6

Invert it :Ā -0.6

Add 0.5 onto it :Ā -0.1

And thats the calculated value you're going to use in theĀ interceptĀ part of the filter. TheĀ slopeĀ is the original contrast value you want to use.

  <feComponentTransfer in="sourceImage">
      <feFuncR type="linear" slope="1.2" intercept="-0.1"/>
      <feFuncG type="linear" slope="1.2" intercept="-0.1"/>
      <feFuncB type="linear" slope="1.2" intercept="-0.1"/>
  </feComponentTransfer>

Contrast Filter

Plug it into the full code to effect the image and you've got this.

<svg viewBox="0 0 1500 1000">
    <defs>
        <filter id="enhanceTheContrast">
            <feComponentTransfer in="sourceImage">
                <feFuncR type="linear" slope="1.2" intercept="-0.1"/>
                <feFuncG type="linear" slope="1.2" intercept="-0.1"/>
                <feFuncB type="linear" slope="1.2" intercept="-0.1"/>
           </feComponentTransfer>
        </filter>
    </defs>
    
  <image xlink:href="bigben.jpg" width="1500" height="1000" filter="url(#enhanceTheContrast)"></image>
</svg>

šŸ”Ŗ Sharpening

Yeah! That's right. You can sharpen SVG images too. That's pretty bad-ass if you ask me.

And it's another one liner to boot.

<feConvolveMatrix in="SourceGraphic" order="3" preserveAlpha="true" kernelMatrix="-1 0 0 0 4 0 0 0 -1"/>

Sharpening Filter

Playing with theĀ -1Ā up and down can increase or decrease the sharpness. Play around with the numbers to see what you can come up with.

<svg viewBox="0 0 1500 1000">
    <defs>
        <filter id="sharpenTheImage">
            <feConvolveMatrix 
                in="SourceGraphic" 
                order="3" 
                preserveAlpha="true" 
                kernelMatrix="-1 0 0 0 4 0 0 0 -1"
            />
        </filter>
    </defs>
    
  <image xlink:href="bigben.jpg" width="1500" height="1000" filter="url(#sharpenTheImage)"></image>
</svg>

šŸŸ¢ Hue Rotation

Lets get funky in this mother... Rotate the hue to change the colours of the image. Very simple effect too.

<feColorMatrix type="hueRotate" in="SourceGraphic" values="45"/>

Hue Rotate Filter

Define theĀ degreesĀ of the hue rotation with the values. Gives some funky results too.ā €


šŸŸ„ Overlays & Blending Modes

OK, so we've gotten this far with just some simple filters. Now we need to add a couple more elements to achieve what we want. Again, I'm going to try to keep this as simple as possible.

So, we want Ā two things this time.

  1. A solid overlay of a particular colour.
  2. A filter to blend the image and the solid overlay.

Let's tackle the solid overlay. This is called aĀ FloodĀ filter effect.

  <filter id="blendingTest">
    <feFlood 
        flood-color="#FF0000"
        flood-opacity="0.9"
        result="floodOut"
    ></feFlood>
  </filter>

Flood Filter

The flood has a specific colourĀ #FF0000Ā in this instance. Notice is also has aĀ resultĀ flag too. This is for chaining multiple filter effects together. One filter'sĀ resultĀ can be another'sĀ inĀ or input.

Just to add to the spice, we can also add flood-opacity too, which is a nice little addition to control the amount of the colour.

Secondly, the blending of theĀ feFloodĀ and theĀ sourceGraphicĀ with a blend-mode.

  <filter id="blendingTest">
    <feFlood 
        flood-color="#FF0000"
        flood-opacity="0.0"
        result="floodOut"
    ></feFlood>
    <feBlend mode="multiply" in="SourceGraphic" in2="floodOut"/>
  </filter>

Blend Mode Added

So now, we have two in's...Ā inĀ andĀ in2. These are now blended together with the 'multiply' blend-mode.

So the full code would be:

<svg viewBox="0 0 1500 1000">
    <defs>
          <filter id="blendingTest">
                <feFlood 
                    flood-color="#FF0000"
                    flood-opacity="0.95"
                    result="floodOut"
                ></feFlood>
            <feBlend mode="multiply" in="SourceGraphic" in2="floodOut"/>
          </filter>
    </defs>
    
  <image xlink:href="bigben.jpg" width="1500" height="1000" filter="url(#blendingTest)"></image>
</svg>

ā›“Chaining Filters Together

You got a little taste in the previous 'overlay' example, but the crux is that you can chain multiple effects together to create one epic filter. To do this, you specify aĀ resultĀ keyword that is then used on theĀ inĀ of the next one.

So chaining three of the previous filters together should be easy. Let's put together:

  1. Saturate
  2. HueRotate
  3. Sharpen
<filter id="epicTriple">
    <feColorMatrix 
        in="SourceGraphic"
        type="saturate" 
        values="3"
        result="saturateOUT"
    />
    
    <feColorMatrix 
        in="saturateOUT"
        type="hueRotate" 
        values="45"
        result="hueOUT"
    />
    
    <feConvolveMatrix 
        in="hueOUT" 
        order="3" 
        preserveAlpha="true" 
        kernelMatrix="-1 0 0 0 4 0 0 0 -1"
        result="sharpenOUT"
    />
    
</filter>

As you can see, I've labeled each effectĀ inĀ andĀ resultĀ and chained them together.

Image >>> saturateOUT >>> hueOUT >>> sharpenOUT

This gives us the code:

<svg viewBox="0 0 1500 1000">
    <defs>
          
        <filter id="epicTriple">
            <feColorMatrix 
                in="SourceGraphic"
                type="saturate" 
                values="3"
                result="saturateOUT"
            />

            <feColorMatrix 
                in="saturateOUT"
                type="hueRotate" 
                values="45"
                result="hueOUT"
            />

            <feConvolveMatrix 
                in="hueOUT" 
                order="3" 
                preserveAlpha="true" 
                kernelMatrix="-1 0 0 0 4 0 0 0 -1"
                result="sharpenOUT"
            />

        </filter>
    </defs>
    
  <image xlink:href="bigben.jpg" width="1500" height="1000" filter="url(#epicTriple)"></image>
</svg>

šŸ““ Black & White

Theres actually a bunch of ways to do this. I'll give you four here.

First, just desaturate the image completely toĀ 0. Easy-as. We've done this above.

<svg viewBox="0 0 1500 1000">
    <defs>
        <filter id="blackAndWhite">
            <feColorMatrix 
                type="saturate" 
                in="SourceGraphic" 
                values="0"
            ></feColorMatrix>
        </filter>
    </defs>
    
  <image xlink:href="bigben.jpg" width="1500" height="1000" filter="url(#blackAndWhite)"></image>
</svg>

Simple Black & White Filter

Secondly, we can use the more complex colourMatrix to control the RGB values that make the image up. This is using the red channel to desaturate the image.

<svg viewBox="0 0 1500 1000">
    <defs>
        <filter id="blackAndWhiteRed">
            <feColorMatrix type="matrix" values="
                .33 0 0 0 0
                .33 0 0 0 0
                .33 0 0 0 0
                 0  0 0 1 0">
            </feColorMatrix>
        </filter>
    </defs>
    
  <image xlink:href="bigben.jpg" width="1500" height="1000" filter="url(#blackAndWhiteRed)"></image>
</svg>

Red-Channel B&W

We have the green channel:

<svg viewBox="0 0 1500 1000">
    <defs>
        <filter id="blackAndWhiteGreen">
            <feColorMatrix type="matrix" values="
                0 .33 0 0 0
                0 .33 0 0 0
                0 .33 0 0 0
                0   0 0 1 0">
            </feColorMatrix>
        </filter>
    </defs>
    
  <image xlink:href="bigben.jpg" width="1500" height="1000" filter="url(#blackAndWhiteGreen)"></image>
</svg>

Green Channel B&W

And the Blue Channel:

<svg viewBox="0 0 1500 1000">
    <defs>
        <filter id="blackAndWhiteBlue">
            <feColorMatrix type="matrix" values="
                0 0 .33 0 0
                0 0 .33 0 0
                0 0 .33 0 0
                0 0   0 1 0">
            </feColorMatrix>
        </filter>
    </defs>
    
  <image xlink:href="bigben.jpg" width="1500" height="1000" filter="url(#blackAndWhiteBlue)"></image>
</svg>

Blue Channel B&W


šŸŒ Instagram Effects

Take a look atĀ Una KravetsĀ fantastic work onĀ CSSGramĀ so you can see what's possible with CSS and how she's recreated famous Instagram filters.

Thanks to her generosity, we can take a look at the underlying SCSS code she used to make those effects on her github repository:

Let's take a look at one of her effects and re-create it now using SVG filters:

The 'aden' effect is right here:Ā https://github.com/una/CSSgram/blob/master/source/scss/aden.scss

[

una/CSSgram

CSS library for Instagram filters. Contribute to una/CSSgram development by creating an account on GitHub.

GitHubuna

](https://github.com/una/CSSgram)

In it, you can see she used the following CSS filter:

@mixin aden($filters...) {
  @include filter-base;
  filter: 
     hue-rotate(-20deg) 
     contrast(.9) 
     saturate(.85) 
     brightness(1.2) 
     $filters;

  &::after {
    background: 
    linear-gradient(to right, rgba(66, 10, 14, .2), transparent);
    mix-blend-mode: darken;
  }

  @content;
}

Alright. A hue-rotate, contrast, saturate, brightness and a colour overlay with a 'darken' blend mode. With all our previous knowledge, I'm sure we can recreate that.

I'm also going to add on a sneaky sharpen too - just for my own effect.

Chaining the filters together is now as easy as pie:

<svg viewBox="0 0 1500 1000">
    <defs>
    <filter id="aden" filterUnits="objectBoundingBox">
    
    <!-- HUE ROTATE -20deg -->
    <feColorMatrix 
        type="hueRotate" 
        in="SourceGraphic" 
        values="-20"
        result="hueRotateOut" 
    />
     
    <!-- DESATURATE 0.85 -->
    <feColorMatrix 
        type="saturate" 
        in="hueRotateOut"  
        values="0.85"
        result="saturateOut"
    />
     
    <!-- BRIGHTNESS 0.9 -->
    <feComponentTransfer in="saturateOut" result="brightnessOut">
        <feFuncR type="linear" slope="0.9"/>
        <feFuncG type="linear" slope="0.9"/>
        <feFuncB type="linear" slope="0.9"/>
    </feComponentTransfer>

    <!-- CONTRAST 1.2 -->
    <feComponentTransfer in="brightnessOut" result="contrastOut">
        <feFuncR type="linear" slope="1.2" intercept="0.05"/>
        <feFuncG type="linear" slope="1.2" intercept="0.05"/>
        <feFuncB type="linear" slope="1.2" intercept="0.05"/>
    </feComponentTransfer>

    <!-- FLOOD OVERLAY rgba(66, 10, 14, .2) -->
    <feFlood 
        flood-color="#420A0E" 
        flood-opacity="0.2" 
        out="floodOut" 
    />

    <!-- BLEND FLOOD & CONTRASTOUT -->
    <feBlend 
        mode="darken" 
        in="contrastOut" 
        in2="floodOut" 
        result="blendOut"
    />

    <!-- SHARPEN -->
    <feConvolveMatrix 
        in="blendOut"
        order="3" 
        preserveAlpha="true" 
        kernelMatrix="-1 0 0 0 4 0 0 0 -1"
        result="sharpenOut" 
    />

  </filter>
  </defs>
    
  <image xlink:href="bigben.jpg" width="1500" height="1000" filter="url(#aden)"></image>
</svg>

Which gives us this wonderful result:

<feComponentTransfer in="saturateOut" result="brightnessOut">
    <feFuncR type="linear" slope="0.9"/>
    <feFuncG type="linear" slope="0.9"/>
    <feFuncB type="linear" slope="0.9"/>
</feComponentTransfer>

<feComponentTransfer in="brightnessOut" result="contrastOut">
    <feFuncR type="linear" slope="1.2" intercept="0.05"/>
    <feFuncG type="linear" slope="1.2" intercept="0.05"/>
    <feFuncB type="linear" slope="1.2" intercept="0.05"/>
</feComponentTransfer>

<feFlood flood-color="#420A0E" flood-opacity="0.2" out="floodOut"  />

<feBlend mode="darken" in="contrastOut" in2="floodOut" result="blendOut" />

<feConvolveMatrix in="blendOut" order="3" preserveAlpha="true" kernelMatrix="-1 0 0 0 4 0 0 0 -1" result="sharpenOut" /> 

Just for reference, all of her effects are here:Ā https://github.com/una/CSSgram/tree/master/source/scss


Final Thoughts

During this little project I've really only brushed the surface of what's possible and I'm blown away by the power of SVG image manipulation.

For the LondonParkour project I wrote the SVG data out into a file and then used Imagick and Inkscape to render out to a PNG and JPG. Now I can start generating images however I like with whatever effects and components I like.

You can send me any questions you like over on my twitter account.

twitter.com/lonetraceur


šŸ“– Quick Reference

<feColorMatrix 
    type="saturate" 
    in="SourceGraphic" 
    values="3"
 />

Saturation Filter

<feComponentTransfer in="sourceGraphic">
    <feFuncR type="linear" slope="0.5"/>
    <feFuncG type="linear" slope="0.5"/>
    <feFuncB type="linear" slope="0.5"/>
</feComponentTransfer>

Brightness filter

  <feComponentTransfer in="sourceImage">
      <feFuncR type="linear" slope="1.2" intercept="-0.1"/>
      <feFuncG type="linear" slope="1.2" intercept="-0.1"/>
      <feFuncB type="linear" slope="1.2" intercept="-0.1"/>
  </feComponentTransfer>

Contrast Filter

<feConvolveMatrix in="SourceGraphic" order="3" preserveAlpha="true" kernelMatrix="-1 0 0 0 4 0 0 0 -1"/>

Sharpening Filter

<feColorMatrix type="hueRotate" in="SourceGraphic" values="45"/>

Hue Rotate Filter

  <filter id="blendingTest">
    <feFlood 
        flood-color="#FF0000"
        flood-opacity="0.0"
        result="floodOut"
    ></feFlood>
    <feBlend mode="multiply" in="SourceGraphic" in2="floodOut"/>
  </filter>

Flood & Blend Mode Filter

<svg viewBox="0 0 1500 1000">
    <defs>
        <filter id="blackAndWhite">
            <feColorMatrix 
                type="saturate" 
                in="SourceGraphic" 
                values="0"
            ></feColorMatrix>
        </filter>
    </defs>
    
  <image xlink:href="bigben.jpg" width="1500" height="1000" filter="url(#blackAndWhite)"></image>
</svg>

Simple Black & White Filter

<svg viewBox="0 0 1500 1000">
    <defs>
        <filter id="blackAndWhiteGreen">
            <feColorMatrix type="matrix" values="
                0 .33 0 0 0
                0 .33 0 0 0
                0 .33 0 0 0
                0   0 0 1 0">
            </feColorMatrix>
        </filter>
    </defs>
    
  <image xlink:href="bigben.jpg" width="1500" height="1000" filter="url(#blackAndWhiteGreen)"></image>
</svg>

Green Channel B&W

<svg viewBox="0 0 1500 1000">
    <defs>
        <filter id="blackAndWhiteBlue">
            <feColorMatrix type="matrix" values="
                0 0 .33 0 0
                0 0 .33 0 0
                0 0 .33 0 0
                0 0   0 1 0">
            </feColorMatrix>
        </filter>
    </defs>
    
  <image xlink:href="bigben.jpg" width="1500" height="1000" filter="url(#blackAndWhiteBlue)"></image>
</svg>

Blue Channel B&W

Finish

As you can see, SVGs are wonderful at post-processing. Being able to add non-destructive filters onto an image will definitely be the future of image processing on the web.