The Time I Wrote a White Noise Generator To Improve Loading Times
Tags: javascript, webdev
Published: 2020-11-28 13:10:00

The year was 2011. I was a young, ~~aspiring~~ software developer working in my first position in the industry.
In our app we had a web view, which we could use to display dynamic content. As mobile connections were at 2011 speeds, we had a hard limit on the size of each web view page - including all assets and code. That was usually not a problem, until one day I had to implement a design where the background was a just-slightly-grainy grey.

Grainy = white noise.

That was a problem. To understand why, let's talk about image compression.

## Image Compression in a Nutshell

The images you see on the web are usually compressed; they can use many different methods in order to save space, but all of them can be categorised into two groups:
- Lose some of the data (This is called **lossy compression**, and includes [JPEG](https://en.wikipedia.org/wiki/JPEG#JPEG_compression) and [GIF](https://en.wikipedia.org/wiki/GIF#Compression_example), among others)
- Represent the data in a way that takes less space (this is called *lossless* compression, and you might best know it through the [PNG](https://en.wikipedia.org/wiki/Portable_Network_Graphics#Compression) format)

Lossy compression of images is commonly visible. [JPEG artifacts](https://commons.wikimedia.org/wiki/File:Asterisk_with_jpg-artefacts.png) are a known side effect of JPEG compression, and in GIFs you could recognise "rounding up" of colors, as it's limited to 256 colors. these methods could work fine for many things (e.g. photography in JPEG), but might be too much where small, clean lines/details are needed. Our white-noise-textured background fell in the second category.

If not JPEG or GIF, I tried to save the background as PNG. The file was BIG.

## You Can't (Losslessly) Compress White Noise

The main method I know for lossless compression involves finding repeating patterns, replacing them with a shorter representation and keeping the replacement in a dictionary. For example, in the string `aaabaaacaaad`, we have the substring `aaa` appearing a few times.
We can shorten it by replacing it with a single character; let's call it 1. We now have a compressed string - `1b1c1d`, and a dictionary - `1:aaa`

When displaying the string, we use the dictionary to replace the shorter substring back to the original one, and get `aaabaaacaaad` again.

Different formats differ in the way they find what to replace, how many times they return the process, and what of many other optimisations they use. However, these basics are commonly used.

Back to our noisy image - white noise is, by definition, random; it is a noise generated by receiving different values in a range with equal probability. Being random, the chance for repeating patterns is very low, and lossless compression in the way we described is not very effective.

## What If We Won't Load This At All?

We'll still have noise, don't worry, but we'll have to deal with it differently:

- Load the page, with a background color or image, but without the noise.
- Use a canvas to generate white noise
- Give the canvas a very low opacity
- Position it above the background
- Mission accomplished!

White noise, being simply a uniform random grey value on each pixel, is very easy to generate. You iterate over the canvas' width and height, and color each pixel in a random shade of grey.

It worked! The total page size was now small enough to fit our hard size limit. However, my state of the art iPhone 4, and all other mobile devices we've tested, took a noticeable time to run this code. This wasn't a good user experience, so we've had to find another solution.

## Tiles

The next plan was to use the existing generator to generate a much smaller area of white noise, and use it as a tile, a repeating background. As it has a low opacity and was used as a texture, people wouldn't easily see it repeats, and we'd have a nice, elegant background texture that is quick to generate.
This also meant that I couldn't simply use the canvas above the background, as I'd then have to fill it. Instead, I used an invisible canvas to generate the white noise tile, then used that tile, in the format of a [data URI](https://css-tricks.com/data-uris/), as a background to a different element.
Thankfully, canvases have long provided this functionality [natively](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL).

This is the process we ended up with:
- Load the page, with a background color or image, but without the noise.
- Use an invisible canvas to generate a low-opacity white noise tile.
- Get the canvas image as a [data URI](https://css-tricks.com/data-uris/), and use that as a [second `background-image`](https://caniuse.com/?search=multiple%20backgrounds) for the container element (together with the untextured image), or as a background-image over a color background.

Since the texture was very subtle, we could get away with texture repetition without it being noticed - as long as the tile wasn't very small. A few attempts to balance tile size (repetition vs. loading speed) and how visible the repetition was, and we've had a working feature :)

## What Should We Take From This?
- It's good to know how things work, even if you only work with them indirectly. By knowing some basics about image compression and white noise, for example, I could find the source of a non-trivial front-end problem, and solve it.
- Beware of focusing too much on the things you directly need for work. Learning a bit about things outside of your daily line of work, in addition to being interesting, could help you out later.