Making Apple Progressive Blur on the Web

Throughout the years, Apple consistently delivers great design. They seem to always define (or at least popularize) a new trend, which then storms the world and everyone copies it. One of the trends, they seem to have doubled down on lately, is progressive blur. Some examples can be seen in the App Store, Apple Maps, and, most recently, the Apple visionOS. Seeing all that made me want to see whether it could also be done for the web (aka for the rest of the world not using swiftUI for everyone else apart from Apple).

Examples of Apple Blur
Examples of Apple usages of progressive blur

The idea came to me when I was redesigning my blog (the one you are reading right now). I’d wanted to make the header blurred, but the currently most popular solution - throw a single blur + solid color with opacity turned slightly down at it and call it a day, was just too boring. I wanted to be like Apple. I wanted progressive blur.

Gradient Blur Since 2009

Progressive blur isn’t anything new. I’ve found mentions of it under the name of ‘gradient blur’ on photoshop forums dated to 2009, or more recently a stackoverflow question from 2017, and a motion guide page from 2019. What I couldn’t find was usages of the progressive blur for the web or any easily usable HTML + css generator for it.

My original solution, for this blog, was based on this codepen. The way it works is by stacking multiple divs with different backdrop filters and gradient masks on top of one other, each blurring the background more and more. It is a great single-use solution and, with some fine-tuning, it produced the desired effect. I wanted more though. I wanted it to be usable for any element, without too much fine-tuning (or at least without fine-tuning it directly inside css).

The Blur Is Not Enough

At this point you may be asking: Why go through all the trouble of stacking blurs? Why not just use a single blur with a single mask? Well. Here is an example of what the effect looks like using only a single blur. The blurred part looks like bloom, the transition from blurred to non-blurred is almost invisible, and if you use a white color for the text (such as Apple uses), the readability of the text drops rapidly, mainly due to the high frequency (rapid transitions between colors) of the background.

Use of a single blur with alpha mask

This is exactly what the progressive blur solves - more blur = lower frequency of the background = better readability of the text. Ok, so we can just blur it more and be done with it, right? Not really. Below is the same example with the blur set to 32px, instead of the original 10px. Now the blur is almost gone, and the image just looks a bit hazy. Essentially, we have blurred it too much and have largely lost the effect we were trying to achieve.

Use of a single blur with alpha mask and too high blur radius
Too high blur radius

Stacking Blurs

My learnings so far were:

  1. the stacking is definitely needed
  2. doing it manually every time is not viable

Only one possible conclusion could have come out of this: I need to make a progressive blur editor with a HTML + css generator.

Using Backdrop Filter

My first step was to take the “static” blur and make the parameters editable in a simple and visual way. I have put together a simple site with some images (to thoroughly test the feel) and some sliders to fine-tune the blur. Internally, there were just multiple divs spawning on top of the image, each having its own backdrop filter with increasing blur radius, and a mask, using a linear gradient with alpha values.

Initial design of progressive blur using backdrop filter
The initial design using backdrop filter

This has worked surprisingly well - it has produced the desired results and is not that heavy on the performance (not that I’ve measured anything, it just feels ok). It has just one tiny little problem. The moment we increase the border-radius of the wrapping element (while using the cutting-edge browser built by google), this happens:

The broken progressive blur on chrome
The result on chrome

It seems that modern css chromium just can’t deal with multiple stacked backdrop filters, masks, and overflow: hidden on the wrapping element. Not entirely sure why, but one answer on stackoverflow gives us some hints. It also made me try it on safari and firefox, both of which, much to my surprise, could have the border radius and progressive blur on at the same time. Chrome is unfortunately the most used browser, so we primarily needed a working solution for that one.

The correct progressive blur on safari and firefox
The result on Safari and Firefox

Blurring Multiple Images On Top Of Each Other

The second solution, which enables us to have border radius even in Chrome, is heavyweight (again, measuring only by vibes, but the page starts to be laggy sometimes, which is a pretty good indication for me), non-optimal, and just plainly shouldn’t be used anywhere on the web. You have been warned.

Visualization of the separate blur layers
The stacked blur layers

This approach achieves the progressive blur by stacking multiple divs with background image set to the original photo, and then applies blur + mask. Since it doesn’t use backdrop filter, there are no shenanigans with the border-radius , which just works. The issue is that it spawns multiple images (that are the size of the original image) with large blur radii (sometimes even 512px), on top of which it does some more operations, like masking. Below is the comparison of the two approaches - backdrop-filter on the left, blur on the right.

Comparison of different blur approaches

They look pretty much the same, except for this part:

Zoom in on the broken part of the blur

This artefact comes from using the blur, which doesn’t reach the edges (as there is no padding for the filter, it is just blurring it with the radius of 512px and not reaching the edge). To deal with this, I have scaled the image up by the factor of 1.2. No special reason to use 1.2, it just worked for most of the cases, while not cutting off too much of the image. It is still a tradeoff, but if we are using the progressively blurred image as a card, we can afford our image to be cut off. Below is the comparison with the scaled image using blurs:

comparison of the approaches, with the scaled image added

Another possible approach would be just to scale the blur. this way it would reach the bottom, still blur the image in the correct parts, and the original image could stay the same. Well, not exactly. It won’t work in our case as the blurred image and the background image must precisely match. The blurred image is just the background image with alpha mask, meaning that at one point it will be trying to blend in with the original image, and any scale or position difference will be noticeable. See for yourself in the images below. On the left, only the blur is scaled, on the right both the blur and the image are scaled.

comparison of the scale approaches, with the broken part highlighted

The Generator

The generator itself is simple. It takes the parameters input through the sliders, does some calculations for the alpha gradients of the masks, blur amounts, and the actual inserted divs with blur, and then displays the image with its blurry overlays. It then takes the parameters and generates the code you can copy to your website.

You can play with it here:

You can see the source code here.