This describes the old version of this project, see Improved Web Component for Pixel-Accurate Atkinson Dithered Images for the new version plus an interactive demo
The code and more technical documentation is available in the as-dithered-image GitHub repository.I have always liked the look of images processed with Atkinson Dithering, the algorithm used in the original Apple Macintosh. There are other ways of crushing a multicolored image down to 1-bit monochrome but I think Atkinson's method produces quite striking results that I thought would look neat on the modern web.
Actually including dithered images on the web is fraught with issues. Dithering relies on a precise correspondence between the source image pixels and the display pixels - any attempt to resize a dithered image is going to be either blurry or covered in distracting Moiré patterns. The browser is actively working against us here - it resizes images in a way that makes sense for most images but totally destroys the sharp dithering effect I am going for.
Even worse, for responsive designs the size of the image can change dynamically. What is needed is a way to hold off dithering until just before the image is to be displayed in the page. By this time we know the exact size of the destination and can dither accordingly, reprocessing the image as its dimensions change.
This is where as-dithered-image comes in - a stand-alone custom element that handles all the annoying details. It is designed to be almost a drop-in replacement for the standard img tag.
And believe me, there are some very annoying details, namely:
- the image is scaled and drawn on a canvas then dithered to the proper size. To get a responsive image, this has to be done each time the image resizes as the user resizes the browser window (try it!)
- it is quite difficult to get true pixel-pixel accuracy on canvas elements on high DPI screens. In particular, Safari likes to use an annoying filter unless the canvas' backing store is exactly the devicePixelRatio times the css screen size. A lot of the other projects I see on the web get this wrong.
- accessibility is important, as-dithered-image supports alt-text for screen readers.
- dithering does not make much sense on screens with really high DPI (like modern iPhones), as-dithered-image attempts to preserve the dithering effect automatically.
There are a few issues with this approach:
- unlike the normal img tag as-dithered-image must be resized exactly, it does not resize itself to the aspect ratio of the source image.
- The dithering is all done in the UI thread. This can lead to janky behavior on resizing if you have a lot of dithered images. The dithering should really be done in a worker thread but I got lazy.