WebRTC Instagram Video Filters

Handwritten by Tian Davis

Instagram

With more than a $1 Billion valuation and over 75 million daily users, Instagram is arguably one of the most popular social networks today. Did you know more than 55 million photos are posted daily on Instagram? Or that the platform sees over 8,500 likes per second?

I’ve always had a small love affair with image processing. So I figured let’s have some fun with WebRTC and find out, “Can you apply Instagram filters to a live WebRTC video stream?” To be brutally honest, the question had been burning me for months, so I finally set out to find the answer.

What I found is indeed you can add Instagram, or rather Instagram-ish, filters to your live WebRTC video stream. Here’s a live demo I put together of the concept (Pro Tip: Use Chrome or FF 35+). This is the story of how I went about getting it done. You can follow along as I break it down step-by-step.

First, we need a little HTML to hang our video element and controls:

<script src="respoke.js"></script>
. . .

<div class="video-streams">
  <video id="localVideo"></video>
  <video id="remoteVideo"></video>
</div>

<p>Instagram Filters:</p>
  
<ul id="filters">
  <li id="NoFilter">#NoFilter</li>
  <li id="Willow">#Willow</li>
  <li id="Earlybird">#Earlybird</li>
  <li id="Mayfair">#Mayfair</li>
  <li id="Amaro">#Amaro</li>
</ul>

Here, I included Google’s adapter.js shim to normalize WebRTC behavior across Chrome, Firefox and Opera. Then, I included a simple video tag. We’ll use that video tag to display the video stream coming from our web camera. We’ll also use that same video tag to apply our image filters against. Lastly, I created an unordered list of Instagram filters and a #NoFilter control to remove all filters from the video element. Interesting to note, #NoFilter is the most popular filter on Instagram. Now for the actual filters.

Laying the foundation for the Instagram filters are CSS3 filters. Thankfully, Nick Georgiou of Design Pieces already did a fantastic job recreating every single Instagram filter using CSS3 filters. Here are a few we’ll use for this experiment:

.ig-willow {
  -webkit-filter: saturate(0.02) contrast(0.85) brightness(1.2) sepia(0.02);
  filter: saturate(0.02) contrast(0.85) brightness(1.2) sepia(0.02);
}

.ig-earlybird {
  -webkit-filter: sepia(0.4) saturate(1.6) contrast(1.1) brightness(0.9) hue-rotate(-10deg);
  filter: sepia(0.4) saturate(1.6) contrast(1.1) brightness(0.9) hue-rotate(-10deg);
}

.ig-mayfair {
  -webkit-filter: saturate(1.4) contrast(1.1);
  filter: saturate(1.4) contrast(1.1);
}

.ig-amaro {
  -webkit-filter: hue-rotate(-10deg) contrast(0.9) brightness(1.1) saturate(1.5);
  filter: hue-rotate(-10deg) contrast(0.9) brightness(1.1) saturate(1.5);
}

Using a combination of CSS3 filters: sepia, saturate, contrast, brightness and hue-rotate; we’re able to approximate Instagram’s Willow, Earlybird, Mayfair and Amaro filters. Now for the really fun part: bringing the app to life with a little JavaScript.

To do this we’ll leverage the camera access feature of WebRTC and then apply the CSS3 filters as we see fit. First, let’s take a look at how we access the camera:

var constraints = {
  video: true, 
  audio: true
};

getUserMedia(
  constraints, 
  onMediaStream, 
  noMediaStream
);

function onMediaStream(stream) {
  localVideo = document.getElementById("localVideo");
  
  attachMediaStream(localVideo, stream);
  
  localVideo.play();
}

function noMediaStream (error) {
  console.log("No media stream for us.", error);
}

Here, we call getUserMedia and pass it three parameters. The first is a constraints object listing which devices to access, in this case, both the video camera and the mic. The last two parameters are callbacks. We use the first callback for when we successfully get a stream from our camera. We use the second callback if accessing the camera stream fails.

Once we have the camera stream, we can then get the video element. Next, we attach the camera stream to the video element. We’ll need to call play on the video element, if we don’t then we’ll just see a camera still shot. Now that we have our video playing, we’ll want to implement a way to apply our Instagram filters to the video element.

To accomplish this, we’ll need to setup an event to apply each filter. Let’s take a look at the JavaScript code to implement this:

var ul = document.getElementById("filters"); 

ul.addEventListener("click", function(e) {
  var filter = e.target.id;
  
  var filters = {
    "NoFilter": function() {
      localVideo.className = "";
    },
    
    "Willow": function() {
      localVideo.className = "ig-willow";
    },
    
    "Earlybird": function() {
      localVideo.className = "ig-earlybird";
    },
    
    "Mayfair": function() {
      localVideo.className = "ig-mayfair";
    },
    
    "Amaro": function() {
      localVideo.className = "ig-amaro";
    }
  }[filter]();
});

Here, we add an event listener to the list of Instagram filters. When a filter is clicked, we’ll get its corresponding filter id. Once we have the filter id, it’s an easy transition to apply the corresponding CSS3 filter to the video element. When we’re ready to remove all filters, we just remove all CSS3 class filters from the video element.

I went ahead and pushed my webrtc video filter code to GitHub in its entirety. The instructions to run it locally are in the README. Git clone and you’ll be up and running in 30 seconds.

So that’s my WebRTC project. As you can see, the video element is a first class HTML5 element. That means you can manipulate it in JavaScript and style it in CSS to your hearts delight. I just think it’s so cool you can manipulate the look and feel of a live video stream.

I’ve heard #Mayfair is one of the top Instagram filters. Which one did you like best? I hope you had as much fun as I did and now you can add Instagram filters to your WebRTC video too.

discuss on twitter


← return to all articles