Recreating Instagram Filter Functionality with CSS and Canvas APIs
Tags: showdev, javascript, css, todayilearned
Published: 2020-06-30 02:21:58

Recreating UI components, especially those in native apps, has always led me to interesting unknown web APIs. It also helps me see things from the app developers' perspective and understand why a particular task is done in a particular way. Going forward, I will jot down the making-of of each clone and share the lessons I've learned. I hope you will also learn a thing or two and starting cloning your favorite components.

Today, I want to share my recent clone of the Instagram Filter page which turned out to be much simpler than I expected (*if I ignore a browser*).

> **Goal**: Users can select a photo from their device, apply filters, see preview, and export(download) the result.

{% youtube aUkKEbRxj2M %}

**[Live Demo](https://n35w3.csb.app/)**

**[CodeSandbox](https://codesandbox.io/s/nifty-lichterman-n35w3?fontsize=14&hidenavigation=1&theme=dark)**

## The Process

Each Instagram filter is made of a set of basic filter effects, e.g. brightness, contrast, saturate, etc, and some overlays. With the help of CSS `filter` and `mix-blend-mode`, stacking filters and overlays to recreate an Instagram filter is pretty much an eyeballing task. Thanks to [this brilliant work](https://una.im/CSSgram/) by [Una](https://twitter.com/una) which did exactly that, 75% of my goal is completed. From there, I only have to figure out a way to export the result since the CSS is changing the appearance but not the actual image. Luckily, I found there are two Canvas APIs that do very similar things and they are canvas `filter` and `globalCompositeOperation`. With them, I can perform the same operation to the image drawn on canvas and use the method `toDataURL` to export it out. 🍬

> Unfortunately, the canvas filter API is experimental and is only supported in Chrome, Firefox, Edge, but not in Safari. So the clone is not fully functioning on the iPhone. **_augh, safari!_**

## Some Details

You can see the full implementation in [the CodeSandbox](https://codesandbox.io/s/nifty-lichterman-n35w3?fontsize=14&hidenavigation=1&theme=dark). Note that the exporting function does not work in CodeSandbox's `iframe` browser, open the app in a new window instead. Here are some implementation details:

I used an array to store the filter configurations:

```javascript
// effects.js
const effects = [
  { 
    name: "noraml", 
    filter: "none", 
    overlays: []
  },
  {
    name: "clarendon",
    filter: "contrast(1.2) saturate(1.35)",
    overlays: [
      {
        backgroundColor: "rgba(127, 187, 227, 0.2)",
        mixBlendMode: "overlay"
      }
    ]
  },
]
```

When the "clarendon" effect is turned into HTML and CSS:

```html
<!--image with "Clarendon" filter applied -->
<figure style="filter: contrast(1.2) saturate(1.35);">
  <img src="/plitvice-lakes.jpg">
  <div style="background-color: rgba(127, 187, 227, 0.2); mix-blend-mode: overlay;"></div>
</figure>
```

This is how a Instagram filter(I called it `effect` in the code) is applied on canvas:

```javascript
function applyEffect(name) {
  // find effect by name
  const effect = effects.find(eff => eff.name === name);
  const { width, height } = previewCanvas;
  // clear canvas
  ctx.clearRect(0, 0, width, height);
  // apply filter
  ctx.filter = effect.filter;
  // draw the image
  ctx.globalCompositeOperation = "source-over";
  ctx.drawImage(previewImg, 0, 0, width, height);
  // loop through overlays and fill with corresponding color and blend mode
  effect.overlays.forEach(overlay => {
    ctx.globalCompositeOperation = overlay.mixBlendMode;ctx.fillStyle = overlay.backgroundColor;
    ctx.fillRect(0, 0, width, height);
  });
}
```

## Lessons Learned

- CSS `filter` and `mix-blend-mode` are handy if you want to alter the look of your page without reaching for graphics editors. They can be applied to not only image but every element.
- Canvas declarative filter API lower the entry barriers to image processing.
- These APIs all make use of the GPU so they are performant.
- The CSS properties have wide browser support but not the canvas `filter` API.
- For production, use WebGL or a thrid-party library instead.
- ~~Safari is the new IE.~~

## Afterthoughts

Since I had played with WebGL before, I am well aware these effects can be achieved using WebGL. (Take a look at `gl-react` if you are a React developer) But this time, I am experimenting with an even simpler solution. And thanks to the declarative APIs (and *also CSSgram!*), recreating those Instagram effects and the export function is not complicated at all. I am interested in how you are using these CSS properties so please let me know! 😉

Thank you for reading! *Until next time! 👋*

![React Summit](https://dev-to-uploads.s3.amazonaws.com/i/2ub10uyfjmw681ixyaae.png)

> React Summit is coming back on October 15-16. There'll be speakers like [Kent C. Dodds](https://twitter.com/kentcdodds), [Max Stoiber](https://twitter.com/mxstbr), [Sara Vieira](https://twitter.com/nikkitaftw), [Sharif Shameem](https://twitter.com/sharifshameem).
> 
> Register for free before September 20: [https://ti.to/gitnation/react-summit?source=REFURCR-1](https://ti.to/gitnation/react-summit?source=REFURCR-1).