The Ultimate Guide to Fast-loading Websites with BlurHash

Have you ever waited for a website to fully load for ages?

Have you loathed the ugly grey boxes and wondered if your internet connection was down?

Did you think to yourself there must be a better solution to show that images are still loading, while having a good user experience at the same time?

If you want your website or app to load lightning-fast but still use your beautiful visual design, BlurHash is the solution you’ve been searching for.

In this article, you’ll learn what BlurHash is and why it’s the best way to achieve fast-loading websites and apps.

What Is BlurHash and Why It’s Important

BlurHash represents a compact placeholder for an image. It consists of a few bytes of data and is used to fill the box of the image while the webpage is loading.

https://pub.dev/packages/flutter_blurhash

It replaces the boring grey boxes with beautiful, vibrant BlurHash states.

The generated strings are quite short. This means you can store them in a data format of your choice. For example, you can add them as a field in a JSON object and store them in your database without much overhead.

How does BlurHash achieve this?

The BlurHash algorithm takes an image and calculates a short string that represents the placeholder for this image. The string is only 20-30 characters long.

Your backend does all the calculations. You can store the image, along with the generated string, in your database.

Then, when you need to send the data to your client, you transfer both the URL to the image and the BlurHash string. Your client takes the hash string and decodes it into an image. The interface shows this image while the full-size version loads in the background.

Since the BlurHash placeholder images are tiny in size, using them dramatically improves the loading speed of your website or app. And with that, user satisfaction skyrockets.

The implementations are small and easy to port to new languages.

And the best part?

Your end-users get a smooth and vibrant experience while waiting for your full design to load. They’ll never start wondering if your website is broken.

Instead, they’ll see the intermediate states and know that the design is actively loading.

BlurHash vs. Other Placeholder Techniques

Of course, there are other placeholder techniques you can use to show users that your website is still loading.

The most popular ones include:

  1. Using no placeholder at all.
  2. Using a thumbnail.
  3. Using a Base64 representation of the image.

Here are the problems with these solutions.

If you’re not using a placeholder for your images, your website appears broken to the end user. Especially when using slow mobile internet, this looks horrible and the user experience is terrible.

You might think using just a thumbnail of the full-size image will solve these problems. But thumbnails are indeed quite large in comparison to BlurHashes.

And, once you stretch a thumbnail to the actual size of the image, your design looks ugly, making the user experience terrible again.

So, we come to the last option – using a Base64 representation of the image. The problem is, this encoding method still presents a lot of overhead and additional data that you need to store in your database and transfer.

The resulting strings are much longer and more difficult to calculate than the Base83 short strings which BlurHash uses.

BlurHash’s Base83 placeholder technique is the most compact one. It not only leads to faster page loading but to the best possible user experience as well.

How Is BlurHash Calculated

Now, we’re coming to the nitty-gritty of what the algorithm looks like.

As we mentioned above, BlurHash placeholders are represented by 20 to 30 characters. They are calculated using a Base83 binary-to-text encoding.

First, BlurHash applies a simple Discrete Cosine Transform (DCT) to the image data, keeping only the first few components. Then, the algorithm calculates these components using a Base83 encoding. It uses a character set that is safe for JSON, HTML, and shell.

The DC component, which represents the average color of the image, is stored as an sRGB value. At the same time, the AC components are encoded lossily[SD11] [NW12] .

Structure of the BlurHash String

Here’s an example of a BlurHash string, with the different parts labelled and explained below:

Example:         LlMF%n00%#MwS|WCWEM{R*bbWBbH

Legend:           12333344....................

  1. Number of components (1 digit).

For a BlurHash with nx components along the X-axis and ny components along the Y-axis, this value is equal to (nx - 1) + (ny - 1) * 9.

  1. Maximum AC component value (1 digit).

All AC components are scaled by this value. It represents a floating-point value of (max + 1) / 166.

  1. Average color (4 digits).

The algorithm takes the average color of the image in sRGB space, encoded as a 24-bit RGB value, with R in the most significant position. This value can be used directly if you only want the average color rather than the full DCT-encoded image.

  1. AC components (2 digits each, resulting in nx * ny - 1 components in total).

The AC components of the DCT transform are sorted in ascending order – first by X, then by Y.

After that, they are encoded as three values for R, G, and B. Each value lies between 0 and 18. They are combined together as R * 19^2 + G * 19 + B, for a total range of 0 to 6859.

Each value represents a floating-point number between -1 and 1. For example, 0-8 represent negative values, 9 represents zero, and 10-18 represent positive values.

Positive values are encoded as ((X - 9) / 9) ^ 2, while negative values are encoded as -((9 - X) / 9 ) ^ 2. Here, ^ represents exponentiation.

The resulting value is then multiplied by the maximum AC component value from field 2.

Base83 Encoding

BlurHash uses a custom Base83 encoding.

The values are encoded individually using 1 to 4 digits and concatenated together.

Multi-digit values are encoded in a big-endian order with the most significant digit first.

The character set used is the following: 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~.

Discrete Cosine Transform

To decode a single pixel of output, the algorithm loops over the DCT components and calculates a weighted sum of cosine functions.

For a normalized pixel position x, y, with each coordinate ranging from 0 to 1, and components Cij, for each of R, G and B, it calculates using the formula below.

The following example illustrates this in pseudocode:

foreach j in 0 ... ny - 1

foreach i in 0 ... nx - 1

value = value + Cij * cos(x * i * pi) * cos(y * j * pi)

Here, the C00 component is the DC component, while the others are the AC components. The DC component must be converted from sRGB to linear RGB space. The AC components are already linear.

Once the R, G, and B values have been calculated, they must be converted back from linear to your output color space, usually sRGB.

Implementations of BlurHash

The algorithm is quite simple. It consists of less than 200 lines of code. There is an even easier way to implement this feature on your website by using a responsive image plugin, which supports BlurHash.

The best part about BlurHash is that you can easily import it to your favorite programming language or platform of choice.

Here’s a list of the currently available implementations and BlurHash image plugins, both our own and third-party:

  • C: An encoder implementation in portable C code.
  • Swift: An encoder and a decoder implementations, including a larger library with advanced features.
  • Kotlin: A decoder implementation for Android.
  • TypeScript: An encoder and a decoder implementations.
  • Python: An integration of the C encoder code into Python.
  • Pure Python: Implementations of both the encoder and the decoder in pure-Python.
  • Go: Two versions of the implementations of the algorithm exist.
  • PHP: An encoder and a decoder implementations in pure-PHP.
  • Java: An encoder implementation in Java.
  • Clojure: An encoder and a decoder implementations in Clojure.
  • Nim: An encoder and a decoder implementations in pure-Nim.
  • Rust and WebAssembly: An encoder and a decoder implementations in Rust. They’re distributed as both native-Rust and WebAssembly packages.
  • Ruby: An encoder implementation in Ruby.
  • Elm: An encoder and a decoder in Elm.
  • Dart: An encoder and a decoder implementation in pure-Dart.
  • .NET: An encoder and a decoder in C#.
  • JavaScript (without framework): implementation via BlurHash responsive plugin.
  • React: implementation via BlurHash responsive plugin.
  • Vue: implementation via BlurHash responsive plugin.

Before we end this article, we want to address a few questions you might have.

Why Did We Use Base83?

The first reason is that Base83 provides the maximum low-ASCII characters which are safe for use in JSON, HTML, and shell.

The second is that 83 * 83 is a little more than 19 * 19 * 19, making it ideal for encoding three AC components in two characters.

Why Didn’t We Use the Full Unicode Character Set?

Using UTF-8 would have been more efficient for encoding.

And perhaps the overhead wouldn’t have been too big.

However, the encoding and the decoding would have been a lot more complicated.

So, for the sake of simplicity, BlurHash uses the easier option – Base83.

How Fast Are Encoding and Decoding?

The current implementations are not optimized for speed.

Therefore, producing hash strings on large images takes a long time. Same goes for decoding the placeholders.

But, here’s the trick to using the algorithm efficiently:

Do not run it on full-sized data.

The fine detail of an image is thrown away anyway, so you should scale down your images before running BlurHash on them.

The best option is to use the thumbnails of your images. Run BlurHash on them instead of encoding the full data.

For decoding, you can use small placeholder images of 20-32 pixels width and let the UI layer scale them up. The result is indistinguishable from decoding them at full size.

BlurHash - the Future of Quick-Loading Websites

Empty boxes instead of images aren't a good user experience. Solve this issue in a very fine way by using the BlurHash placeholders. Do you want to give it a try and skyrocket your website's performance?

Check out our free BlurHash plugins: