Skip to content

Dithering

renderImage quantises each pixel to either black (1) or white (0). For text and line art a hard threshold is usually best — every pixel ends up where the source put it. For photographs and other continuous-tone input, a hard threshold collapses gradients into solid blobs; a dither method scatters the quantisation error across nearby pixels so the printed average roughly matches the input greyscale.

This page renders the same source image through every available method so you can pick by eye. For an at-a-glance cheat sheet, jump to § When to use what.

How to choose one

Pass a dither option to renderImage:

ts
renderImage(rgba, { dither: 'atkinson' });   // named method
renderImage(rgba, { dither: false });        // hard threshold (default)
renderImage(rgba, { dither: true });         // alias for 'floyd-steinberg'

Available methods: 'floyd-steinberg', 'atkinson', 'stucki', 'jarvis-judice-ninke', 'bayer4', 'bayer8'. The boolean form exists for v1.0 compatibility.

Logo (sharp two-tone)

Logos and icons usually want hard threshold — every method other than false introduces speckle on the solid fills.

Line art

Thin strokes and dots break up under any error-diffusion method. Hard threshold preserves them; ordered (Bayer) methods leave them mostly intact but add a uniform texture to the background.

Photograph

This is where the dither methods earn their keep. Hard threshold collapses the sky into a single solid; Bayer adds a strong checkerboard pattern that some people find period-appropriate; the error-diffusion family produces the most "photographic" output, with subtle differences:

  • Floyd–Steinberg — the workhorse default. Slightly directional artefacts on smooth areas.
  • Atkinson — only diffuses 6/8 of the error, dropping the rest. Higher-contrast result; cleaner edges; can look "punchy" on faces.
  • Stucki / JJN — bigger kernels, smoother gradients, slightly slower.

When to use what

Content typeRecommended
Text, QR, barcodes, line drawingsdither: false (hard threshold)
Logos with sharp fillsdither: false, or 'atkinson' if anti-aliased
Photos — smoothest gradientdither: 'jarvis-judice-ninke' or 'stucki'
Photos — crispest, most contrastdither: 'atkinson'
Photos — balanced defaultdither: 'floyd-steinberg' (or dither: true)
Repeating patterns, posters, retro lookdither: 'bayer4' or 'bayer8'
Speed-critical inner loopdither: 'bayer4' (no error propagation)

Performance notes

Error-diffusion methods (floyd-steinberg, atkinson, stucki, jarvis-judice-ninke) walk every pixel and update its neighbours, so cost scales with image size and kernel width. Bayer methods (bayer4, bayer8) are simple table lookups and are by far the fastest — appropriate when you're rendering many small bitmaps in a tight loop.

For threshold output, see also the threshold option (default 128) — useful when source images aren't centred around mid-grey.

Playground

drop an image, or click to choose
source
output (1bpp)
Code snippet
renderImage(rgba, {
  dither: 'floyd-steinberg',
});

Released under the MIT License.