Blog

Halftone Palette

June 17, 2023

I recently stumbled upon an — old — article about the generation of [halftone QR codes](http://vecg.cs.ucl.ac.uk/Projects/SmartGeometry/halftone_QR/halftoneQR_sigga13.html). It is a very impressive technique where a QR code is locally distorted to recreate an input image while maintaining its data encoding properties. I found the idea really clever, and the black and white images it involves have a really nice feel. It confirmed that the halftone effect, where an image is only composed of either black or white pixels, was something I liked a lot. So I decided to dig further.
3 examples of halftone QR codes
Examples of halftone QR codes, from the teaser of Chu, H. K., Chang, C. S., Lee, R. R., & Mitra, N. J. (2013). Halftone QR codes. ACM Transactions on Graphics (TOG), 32(6), 1-8
# Black and White Halftoning Basically, it was originally developped for printing images. The idea is that, given an input grey-scale image, you can reproduce it with black ink on white paper by projecting little dots everywhere, with a high density in darker regions and a low density in brighter ones. You simply divide the original image into a grid, compute the brightness of each cell, and replace it with a dot with a proportional area. If the grid is small enough, the blurring property of the human eye averages patches of black and white dots into grey areas of varying intensities.
Linear black and white gradient Halftone version of the linear black and white gradient Halftone version of the linear black and white gradient
Left: original image; middle: coarse halftone version with the dividing grid visible; right: Finer halftone version of the linear gradient
From that, many variations are possible to make the result less *tabular*. Most important ones are tilting the grid, and interlacing the rows, ie. horizontally translating one row out of two by half of the cell size.
Coarse  interlaced and tilted halftone gradient Fine interlaced and tilted halftone gradient
Coarse (left) and fine (right) interlaced and tilted halftone images
Also, the dot shape can change: Dot shape | Rendering --------- | --------- Circle | ![Halftone linear gradient with circular dots](https://i.imgur.com/tJtBUy9.png) Ellipse | ![Halftone linear gradient with elliptical dots](https://i.imgur.com/5B9jTE6.png) Horizontal line | ![Halftone linear gradient with horizontal lines](https://i.imgur.com/SKoNhtk.png) Vertical line | ![Halftone linear gradient with verical lines](https://i.imgur.com/voikPx3.png) Triangle | ![Halftone linear gradient with triangles](https://i.imgur.com/jbpYIAO.png) Square | ![Halftone linear gradient with squares](https://i.imgur.com/s4fOcV3.png) Hexagon | ![Halftone linear gradient with hexagons](https://i.imgur.com/BuLb5kC.png) Octagon | ![Halftone linear gradient with octagons](https://i.imgur.com/DNLBSJg.png) Euclidean | ![Halftone linear gradient with euclidean dots](https://i.imgur.com/AgTB87C.png) The euclidean dot is an interesting one. It looks like there is a switch from black dots on white paper to white dots on black paper after 50% darkness. Actually, the grid pattern is maintained, but the dots are allowed to leak outside their cells. In the words of [Kodak](https://workflowhelp.kodak.com/pages/viewpage.action?pageId=33789056&scmLanguageKey=en), the excess ink injected in one cell bridges between the dots. It is particularly suited to high and low key images. One thing that lured me into halftoning images is the generation of pixelated images. In the above examples, dots look smooth, as we would see in paper printing: they were generated in a [JavaScript canvas](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API) which, by default, applies [anti-aliasing](https://en.wikipedia.org/wiki/Spatial_anti-aliasing), which re-introduces grey pixels in the image. For an exactly *black and white* image, we want to turn that off.
Anti-aliased halftone image Aliased halftone image
Anti-aliased (left) and aliased (right) halftone images
# Colored Halftoning Black density can be used to reproduce grey levels. Colors can also be reproduced by varying intensities of a few primary colors, either by using [additive](https://en.wikipedia.org/wiki/Additive_color) (as in a LED screen) or [subtractive](https://en.wikipedia.org/wiki/Subtractive_color) (as in painting) color mixing. Again, the idea is simple. An image is decomposed into several color channels. The basic additive basis is [RGB](https://en.wikipedia.org/wiki/RGB_color_model) (red, green & blue), while the basic subtractive basis is [CMY](https://en.wikipedia.org/wiki/CMYK_color_model#CMY) (cyan, magenta & yellow). For printing, the [CMYK](https://en.wikipedia.org/wiki/CMYK_color_model) (cyan, magenta, yellow & black) basis is often used to offer richer black tones. For each channel, an halftone layer (often called *screen*) is generated. All layers are then mixed together according to some rule. In additive mixing, RGB values are simply added together. In subtractive mixing, RGB values are inversed and subtracted to the first layer, usually white. Also, each screen is tilted differently, to avoid [Moiré patterns](https://en.wikipedia.org/wiki/Moir%C3%A9_pattern). As we only have a 90° range, a group of CMY screens is tilted at 30°, 60° and 75°. Yellow is a brighter color, hence it is less important if it is not tilted enough.
Colored circles Halftone colored circles
Orignal image (left) and colored halftone version (right)
# Getting Creative I really wanted to play with all this but failed at finding a good software for it. I have not searched a lot, so I might have missed something, but a lot of existing solutions are either very basic, do not offer a lot of customization or asking you for money. Therefore, I created my own: [Halftone Palette](https://chalier.fr/halftone-palette/)!

Screenshot of my online halftone palette

It allows for everything (and a little more) detailed in this article; for instance, illustration images were generated with it. You'll find the code — and the possibility to contribute too! — on [GitHub](https://github.com/ychalier/halftone-palette). It is far from perfect, but it allows for playing around. Here are some of the images I was able to generate:

A forestry coast with three colors subtractive mix
A forestry coast with three colors subtractive mix

Window frame, with a coarse grained screen on top of a finer one
Window frame, with a coarse grained screen on top of a finer one

Plane in the sky, with a two color additive mix
Plane in the sky, with a two color additive mix

Building, boosted contrast and euclidean dots
Building, boosted contrast and euclidean dots

Interlaced hills, with a two tone subtractive mix
Interlaced hills, with a two tone subtractive mix

Cloudy bridge, small dots
Cloudy bridge, small dots

Cliff, two colors
Cliff, two colors

Building, vertical lines
Building, vertical lines

Waves, vertical lines and blue background
Waves, vertical lines and blue background

Large input noise
Large input noise

Lighthouse near a cliff, animated by offsetting the grid, with a Bayer-like pattern