Dithering
This is a short article on how to easily dither images if you’re looking for nice retro aesthetics using simple Unix commandline tools. I won’t cover the technical aspects in detail — if you’re interested in them there’s a great article on surma.dev.
Dithering is a set of techniques for adding noise to images which have had their colour palettes reduced. The main reason to do this is that it allows you to reduce the size of an image considerably while still preserving a decent amount of detail. Often, people also do this for retro aesthetics. Two great examples are the wonderful Return of the Obra Dinn by Lucas Pope, and the website ritualdust.com.
How to do it
The easiest way I’ve found to perform the actual dithering is using “didder”, a commandline tool by makeworld. It supports many types of dithering and accepts various helpful parameters. However, it can only accept and output PNG and GIF images, so if you have e.g. JPEG images you will unfortunately have to make them into PNGs.
The installation process simply involves downloading a binary from the
releases page, copying it somewhere in your $PATH
(e.g. /usr/local/bin
), and marking it as executable (i.e. with chmod +x /usr/local/bin/didder
). You should then be
able to use the didder
command.
I’m going to go over some basic options that get good-looking results. If you want to understand the options in more detail, read the didder manpage.
Just for reference, this is the image we’ll be using for testing:
Old-school dithering
The simplest and most retro-looking form of dithering uses an ordered dithering matrix, which specifies a matrix of offsets which will gently nudge each pixel into varying colours of our palette. This is the kind of dithering that was used on the Game Boy. To achieve this with didder, we can run the following command:
Let’s break down the arguments. -i example.png
represents our input image. -o example_bayer16.png
is our output
image. -p 'black white'
defines our dithering palette. Remember, dithering involves reducing the image’s colour palette
to a small number of colours. In this case, we will nudge all of the image’s colours into either black or white.
Lastly, bayer 16x16
represents the algorithm we’re going to use, which is ordered dithering based on a 16x16 Bayer
matrix. This dithering algorithm creates visible patterns in the output, and the size of the matrix will affect these
patterns. You can compare this with the result of using bayer 8x8
:
You can also use the --strength
option to adjust the strength of the dithering. Broadly speaking, you’ll perceive this
as affecting the contrast of the image. A value of 1.0
is the default, so you can try to add or subtract a little bit
from that if the contrast of your image isn’t looking quite right.
Super old-school dithering
If you’d like to go extra old-school, you can downscale the image before dithering, and upscale it afterwards. This gives you a pixelated look, as you would expect.
Nicer-looking dithering
If you’d rather like your images to look more high-quality, an error diffusion matrix method will give better results, at least in my experience. This method works by checking how far it had to nudge the colour of every pixel, and then distributing this error to surrounding pixels. I’ve chosen the Sierra error diffusion matrix, but didder has a bunch of different matrices you’re free to play around with.
There is also an -s
option you can specify to use “serpentine dithering”, which will wiggle the position of the
dithering a bit when going up and down the image. This should reduce patterning, but on my image it didn’t seem to make a
huge difference.
As before, you can try the upscaling method to make things a bit more retro and pixelated.
Colour palettes
So far, our images have only been black and white. It’s possible to specify a larger colour palette, which increases the amount of detail in the image by a lot. To start off with, we’re going to keep the image greyscale, but specify two additional shades of grey in between black and white.
Of course, a lot of the aesthetic value comes from adding a little bit of colour in there. The way this works is that you dither the image just as before, but you recolour each colour in your greyscale palette, by converting it into an arbitrary RGB colour. Here, we’ve palette-swapped the previous image into a blue palette.
You can choose any colours you like. Here’s an example with a palette based on the original Game Boy colours.
At this point, we can try the original Bayer matrix algorithm, just to see what it looks like.
One last thing I’d like to mention is that all this dithering we’ve been doing has obviously reduced our image size by
quite a lot, which is really neat. Our original example.png
was 197K, while the black-and-white Sierra version is 23K,
and our Game Boy palette Sierra version is 44K. This is all with lossless compression, too. I’m sure file size could be
reduced even further, since I wasn’t paying close attention to it while doing these tests.
I encourage you to experiment with the options and to read the article and manpage I linked above in more detail, since the technical details behind dithering are pretty interesting. Send me your results! :)