Pimp My IDE / garage dispatchBack to the garage
October 13, 2026 / interface craft / pixels

Spend the pixel budget on purpose.

Dithering can give a flat interface heat and grit. It can also turn text, edges, and motion into exhaust.

Quantize first. Add a controlled pattern second. Scale with an explicit browser rule. Check the result at the size people will use.

Retro shading is a rendering decision.

Maxime Heckel's long-form guide treats dithering, color quantization, pixelization, and CRT-style cells as separate parts of a post-processing stack. That separation matters. A limited palette creates visible bands. Dithering redistributes the error between those bands. Pixel scaling decides whether the pattern stays sharp when the browser enlarges it.[1]

The effect works because neighboring marks can suggest an intermediate tone that the palette does not contain. On an old display, that solved a hardware limit. On a current website, it is a style choice. The constraint needs a job.

A dither pattern should reveal the shape. If it competes with the label, it is overspray.

Keep the browser's two scaling controls straight.

CSS image-rendering selects how a scaled image is sampled. MDN notes that it does nothing when the image is not scaled. crisp-edges preserves contrast with an algorithm such as nearest neighbor. pixelated uses nearest-neighbor-style scaling to the nearest integer multiple, then smooths to the final size when needed.[2]

Canvas has its own switch. imageSmoothingEnabled is true by default. Set it to false before enlarging a low-resolution canvas when you need hard pixel edges.[3]

Neither setting creates a dither. The pattern must already exist in the source pixels or be produced by your drawing code or shader. Scaling controls only decide how those pixels travel to the screen.

Ordered noise is easier to art-direct.

An ordered Bayer matrix repeats a fixed threshold pattern. The repetition is visible, which is useful when the page wants a mechanical print texture. Random noise breaks the grid but can shimmer if it changes while the content moves. Keep the random field stable between frames unless movement is the intended effect.

Start with four levels per color channel and a 4-by-4 ordered matrix. Increase the palette before shrinking the pattern. A larger palette reduces the pattern's workload. A smaller pixel block reduces its visual weight. Test both on the real background instead of tuning against a checkerboard forever.

ctx.imageSmoothingEnabled = false;

const threshold = (bayer4[y & 3][x & 3] + 0.5) / 16 - 0.5;
const adjusted = channel + threshold * 255 / (levels - 1);
const quantized = Math.round(adjusted / 255 * (levels - 1))
                * 255 / (levels - 1);

Reserve clean lanes for reading.

Do not run full-strength dithering under body copy, small controls, focus rings, or error text. Keep those surfaces solid. Use the treatment on illustrations, large display type, transitions, or bounded panels where its texture has a clear role.

The W3C CSS Images draft defines image-rendering as a scaling preference for images that need to be resized. It also notes that the user agent may choose algorithms that balance speed, quality, and other factors. Treat browser output as something to inspect, not as a promise of identical pixels everywhere.[4]

Ship the plain image first. Add the treatment as a layer. Keep a reduced-motion path if parameters animate. Then look at the result on a dense desktop screen and a narrow phone. Retro is not permission to make the interface harder to read.

Interactive makeover / comparison viewer plus recipe card

Run the paint booth.

This browser-local teaching tool replaces a decorative retro filter. Tune the palette, pattern, and pixel scale while the clean reference stays beside the treated sample. Copy the settings when the texture earns its space.

1Color
2Steps
3Pattern
4Scale

Set the coat

Controls update a generated sample. They do not measure readability or choose a production setting.

Palette levels per channel4 levels
2 LEVELS8 LEVELS
Pixel block3 px
1 PX6 PX
Pattern

Inspect the panels

Four levels and ordered dithering produce a visible mechanical texture. Compare the circle edge and dark gradient.

Clean referenceFull color
Treated sample4 levels / Bayer
Estimated channel palette 64 colorsHard scale / 3 px blocks

The viewer demonstrates a rendering recipe. It does not test contrast, frame time, browser parity, or readability in your interface.

Sources read / October 13, 2026

Source log

Open the technique, browser, specification, and discussion records
  1. Maxime Heckel, "The Art of Dithering and Retro Shading for the Web", published August 6, 2024 and read October 13, 2026. This supports the separation of dithering, quantization, pixelization, and post-processing effects. Its React Three Fiber implementation is not used by the browser-local Canvas 2D sample above.
  2. MDN, image-rendering, read October 13, 2026. This supports the CSS property's scope, the non-scaled-image limit, and the descriptions of crisp-edges and pixelated.
  3. MDN, CanvasRenderingContext2D.imageSmoothingEnabled, read October 13, 2026. This supports the default smoothing state and the sharp-pixel use case when scaling Canvas images.
  4. W3C CSS Images Module Level 3, image rendering, Candidate Recommendation Draft dated December 18, 2023 and read October 13, 2026. This is the standards record for image-scaling preferences and their quality and performance latitude.
  5. Hacker News discussion, item 50068732, verified through the Hacker News API and read October 13, 2026. This was the discovery route. The discussion is not used as technical evidence.

Evidence boundary. The linked article, MDN references, W3C draft, and exact Hacker News record were read. The paint booth uses an independent Canvas 2D implementation with a fixed Bayer matrix and deterministic noise. It is a style study, not a benchmark or compatibility test.