Instagram Filters

Instagram Filters

April 29, 2025
4 min read
Table of Contents

The motivation

Photos with a certain aesthetic tend to attract more attention and engagement from potential buyers. Many real estate photographers are using film-like effects to give their digital photos a unique, warm, and inviting look. However, achieving this consistently across hundreds of property listings is time-consuming, even for experienced photographers and people with resources.

The goal here is to create a component we could use to automatically apply film-like effects to the digital property photos, client side, so we are not actually touching the photos.

The project

Here’s how we can achieve this on Next.js and React:

  1. We will utilize the Canvas API to manipulate the image data.
  2. The simplex-noise will give use a film grain we can use to create a noise pattern that mimics it.
  3. We will think in terms of digital photography:
    • Grain: Adds texture, so it’s an additive assignment.
    • Vignette: It’s a value between 0 and 1, so it’s a multiplicative assignment.
    • Light Leak: It’s as a combination of addition and “blending”, which is the process of combining two color values based on a third parameter called the “blend factor”. Mathematically, is linear interpolation.
    • Brightness, Contrast, Temperature & Vibrance: Are overall tone adjustments that can be easily found by searching for them.
  4. From them, we will create presets like ‘31337 Color 200’ and ‘Superior X-tra 800’ to mimic known film stocks.

The algorithm

In filmEffects.ts we will use simplex-noise to start rewriting the whole image pixel by pixel, client side. This approach gives us precise control over the final look and allows us to apply effects that wouldn’t be possible with CSS filters.

  1. First, we get the image data from the canvas context using ctx.getImageData(0, 0, width, height). This gives us an array where each set of four elements represents the RGBA values of a single pixel.
  2. We then loop through each pixel of the image using nested for loops:
for (let x = 0; x < width; x++) {
  for (let y = 0; y < height; y++) {
    const i = (y * width + x) * 4;
    // Pixel goes here
  }
}
  1. For each pixel, we apply our effects in sequence. For example, we add grain by generating a noise value and adding it to the pixel’s color channels:
const noise = noise2D(x / 2, y / 2) * 0.5 + 0.5;
const grainValue = noise * settings.grain * 255;
  1. We apply all other effects by modifying the pixel values in some way.
  2. After all effects are applied, we cap the values to ensure they stay within the valid range of 0 to 255.
  3. Finally, we put the modified image data back onto the canvas using ctx.putImageData(imageData, 0, 0).

This pixel-by-pixel approach may be computationally intensive for old devices, but I don’t believe it justifies a WebWorker.

The presets

I came to know the valid referencevalues of the filters from this project by Jonas Wagner.

He wrote this algorithm, which uses a more complex logic for color temperature adjustment based on actual color science, including LUTs, while our approach uses a simpler linear adjustment.

The conclusion

Other approaches are possible, specially those based on LUTs, but in my experience, based in the Unity Engine, it comes down to trial and error and taste, so it’s not a big deal. Besides, a lot of people have color deficiencies, me included, so an approach based on models it’s more accesible.

The code

Repository: github.com/feremabraz/instagram-filters

Web: instagram-filters.vercel.app