Creating Instagram-Style Photo Filters With jQuery

I’ve always been intrigued by the Instagram phenomenon and how quickly it rose to popularity. Photo filters are nothing new but dynamic use of these filters has not always been possible. Building the functionality into an iOS/Android application requires a lot of time. Thankfully developers have worked to replicate this process for the web using JavaScript.

In this tutorial I want to demonstrate how to build a simple Instagram-style filter webapp using jQuery. The library is called CamanJS which is easily extendable to create your own filter plugins. This doesn’t require jQuery but I’ve chosen to use it for a simpler development environment. Take a look at my live sample demo to understand the goal of this tutorial.

Getting Started

First I’ve downloaded a local copy of jQuery along with the CamanJS library on Github. I should point out that you must host your files on a localhost or external server in order to test the working effects. This relates to an issue with the origin host of Canvas elements that gets pretty confusing if you’re new to the idea.

To sum up: don’t be surprised if you open the files locally and they don’t work. You will need to get them hosted somewhere to test this out.

My index file has a lot of extra comments relating to how you may implement Caman. I’ve created a list of navigation links which apply steady filters on top of the pre-existing image. Below is a copy of my entire HTML body:

<div id="w"> <div id="content"> <h1>Instagram-Style Photo Filters</h1> <nav id="buttons" class="centered"> <a href="#" id="resetbtn" class="btn">Reset Photo</a> <a href="#" id="brightnessbtn" class="btn">Brightness</a> <a href="#" id="noisebtn" class="btn">Noise</a> <a href="#" id="sepiabtn" class="btn">Sepia</a> <a href="#" id="contrastbtn" class="btn">Contrast</a> <a href="#" id="colorbtn" class="btn">Add Blue</a> </nav> <!-- <div class="centered"><img src="img/city.jpeg" alt="lpixel" id="mainpic" data-caman-hidpi="img/city@2x.jpeg" data-caman="brightness(10) contrast(30) sepia(60) saturation(-30)"></div> --> <div class="centered"><canvas id="maincanvas"></canvas></div> </div><!-- @end #content --> </div><!-- @end #w --> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <div id = "w" > <div id = "content" > <h1> Instagram-Style Photo Filters </h1> <nav id = "buttons" class = "centered" > <a href = "#" id = "resetbtn" class = "btn" > Reset Photo </a> <a href = "#" id = "brightnessbtn" class = "btn" > Brightness </a> <a href = "#" id = "noisebtn" class = "btn" > Noise </a> <a href = "#" id = "sepiabtn" class = "btn" > Sepia </a> <a href = "#" id = "contrastbtn" class = "btn" > Contrast </a> <a href = "#" id = "colorbtn" class = "btn" > Add Blue </a> </nav> <div class = "centered" > <canvas id = "maincanvas" > </canvas> </div> </div> <!-- @end #content --> </div> <!-- @end #w -->

Notice the comment hiding an image with the ID #mainpic. This uses some HTML5 data attributes like data-caman-hidpi and data-caman. You can actually set a number of predefined filters which load immediately onto the photo. This generates a new canvas element right into the page without writing any JavaScript code.

But since we want to delve a bit deeper I’ve created a lone canvas element with the ID #maincanvas. This will be used to load the photo and then apply filters repeatedly until the canvas is reset.

Generating the Canvas Image

Down at the bottom of my HTML page I’ve written all the JavaScript into a pair of tags. Each link has its own click event handler to apply varying degrees of filters onto the canvas.

var canvas = $('#maincanvas'); var ctx = canvas[0].getContext("2d"); var $reset = $('#resetbtn'); var $brightness = $('#brightnessbtn'); var $noise = $('#noisebtn'); var $sepia = $('#sepiabtn'); var $contrast = $('#contrastbtn'); var $color = $('#colorbtn'); 1 2 3 4 5 6 7 8 9 var canvas = $ ( '#maincanvas' ) ; var ctx = canvas [ 0 ] . getContext ( "2d" ) ; var $ reset = $ ( '#resetbtn' ) ; var $ brightness = $ ( '#brightnessbtn' ) ; var $ noise = $ ( '#noisebtn' ) ; var $ sepia = $ ( '#sepiabtn' ) ; var $ contrast = $ ( '#contrastbtn' ) ; var $ color = $ ( '#colorbtn' ) ;

The variable canvas targets the element as a jQuery object, but it’s not the natural canvas element. We obtain the canvas itself by using canvas[0] which is helpful to acquire details using getContext(). Generally speaking, this context allows us to write and rewrite objects onto the canvas from within JavaScript. The filter links get their own jQuery variables so we can quickly identify them on each click event.

$reset.on('click', function(e){ e.preventDefault(); var img = new Image(); img.src = "img/city.jpeg"; ctx.save(); ctx.setTransform(1, 0, 0, 1, 0, 0); ctx.clearRect(0, 0, canvas[0].width, canvas[0].height); ctx.restore(); ctx.drawImage(img, 0, 0); Caman('#maincanvas', 'img/city.jpeg', function(){ this.revert(false); this.render(); }); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 $ reset . on ( 'click' , function ( e ) { e . preventDefault ( ) ; var img = new Image ( ) ; img . src = "img/city.jpeg" ; ctx . save ( ) ; ctx . setTransform ( 1 , 0 , 0 , 1 , 0 , 0 ) ; ctx . clearRect ( 0 , 0 , canvas [ 0 ] . width , canvas [ 0 ] . height ) ; ctx . restore ( ) ; ctx . drawImage ( img , 0 , 0 ) ; Caman ( '#maincanvas' , 'img/city.jpeg' , function ( ) { this . revert ( false ) ; this . render ( ) ; } ) ; } ) ;

Whenever the reset button is clicked the canvas should remove all filters and embed the image back onto the page from scratch. This took a lot of research to figure out how to clear the canvas of all context, and then add the image back without any CamanJS filters. Take a look at this Stack Overflow post which goes into a bit more detail.

Basically we’re resetting the canvas to the same width/height but removing all context. The new context gets redrawn by creating a dynamic image element using JavaScript. Caman has a method named revert() to remove all filters and start over again.

Filter Methods

The rest of my code is blocked into click event handlers. This means whenever a user clicks any of the navigation links we handle the function differently. I’ve incorporated a number of Caman’s default filters but there are so many more worth exploring.

$brightness.on('click', function(e){ e.preventDefault(); Caman('#maincanvas', 'img/city.jpeg', function(){ this.brightness(10); this.contrast(0); this.render(function(){ // some callback function after rendering }); }); }); $noise.on('click', function(e){ e.preventDefault(); Caman('#maincanvas', 'img/city.jpeg', function(){ this.noise(10); this.render(); }); }); $contrast.on('click', function(e){ e.preventDefault(); Caman('#maincanvas', 'img/city.jpeg', function(){ this.contrast(10); this.render(); }); }); $sepia.on('click', function(e){ e.preventDefault(); Caman('#maincanvas', 'img/city.jpeg', function(){ this.sepia(20); this.render(); }); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 $ brightness . on ( 'click' , function ( e ) { e . preventDefault ( ) ; Caman ( '#maincanvas' , 'img/city.jpeg' , function ( ) { this . brightness ( 10 ) ; this . contrast ( 0 ) ; this . render ( function ( ) { // some callback function after rendering } ) ; } ) ; } ) ; $ noise . on ( 'click' , function ( e ) { e . preventDefault ( ) ; Caman ( '#maincanvas' , 'img/city.jpeg' , function ( ) { this . noise ( 10 ) ; this . render ( ) ; } ) ; } ) ; $ contrast . on ( 'click' , function ( e ) { e . preventDefault ( ) ; Caman ( '#maincanvas' , 'img/city.jpeg' , function ( ) { this . contrast ( 10 ) ; this . render ( ) ; } ) ; } ) ; $ sepia . on ( 'click' , function ( e ) { e . preventDefault ( ) ; Caman ( '#maincanvas' , 'img/city.jpeg' , function ( ) { this . sepia ( 20 ) ; this . render ( ) ; } ) ; } ) ;

These are the first four event handlers which add brightness, noise, contrast, and sepia coloring onto the photo. The numeric value passed into the method defines how much strength of the filter should be applied. Numbers typically range from 0-100 but it truly depends on the method being called.

The syntax is very easy to pickup once you’ve really looked at it. Caman() is the initialization method which first takes the canvas selector, followed by the image to be filtered. Then within a callback function we define which filters should be applied. The filters get applied onto the image canvas by using render().

Each of these filters will compound over time. So for example if you click Sepia twice you’ll get two repeated filter effects using sepia tones. I’ve kept everything at a lower value of 10-20 so you can watch the incremental difference.

$color.on('click', function(e){ e.preventDefault(); Caman('#maincanvas', 'img/city.jpeg', function(){ this.colorize("#3c69da", 10); // alternative syntax // this.colorize(60, 105, 218, 10); this.render(); }); }); 1 2 3 4 5 6 7 8 9 $ color . on ( 'click' , function ( e ) { e . preventDefault ( ) ; Caman ( '#maincanvas' , 'img/city.jpeg' , function ( ) { this . colorize ( "#3c69da" , 10 ) ; // alternative syntax // this.colorize(60, 105, 218, 10); this . render ( ) ; } ) ; } ) ;

My last button event appends a very slight color overlay onto the image. This behaves like an opacity filter using a solid color as the background. Notice the syntax will accept RGB or hexadecimal color values. Take a look at Caman’s built-in functionality to find even more filter effects.

Closing

There probably won’t be too many projects where you need to replicate these photo effects. But it’s always fun toying around with new JS libraries, and you never know when it could be applicable. Feel free to download a copy of my source code and see what else you can build. Additionally if you have any questions or comments drop some ideas in the comments below.