Fetching latest headlines…
How I Built a Perceptual Color Quantization Engine for LEGO Mosaics
NORTH AMERICA
🇺🇸 United StatesMay 10, 2026

How I Built a Perceptual Color Quantization Engine for LEGO Mosaics

8 views0 likes0 comments
Originally published byDev.to

The Problem

Converting a photo into a LEGO mosaic sounds simple: resize the image, find the closest LEGO color for each pixel, done.

Except it looks terrible. Skin tones turn green. Hair becomes a muddy blob. The algorithm picks colors that are mathematically close in RGB but perceptually wrong.

The Solution: OKLab Color Space

I switched from RGB to OKLab, a perceptually uniform color space designed in 2020. In OKLab, equal numerical distances correspond to equal perceived color differences.

This alone was a massive improvement, but it wasn't enough.

Material-Aware Matching

LEGO bricks aren't paint. A matte red brick and a transparent red brick look completely different under the same light. My engine models each brick's material properties (matte, transparent, metallic, glitter) and weights the color distance accordingly.

// Simplified: material-aware distance
function colorDistance(pixel, brick) {
  const labDist = oklabDistance(pixel, brick.color);
  const materialPenalty = pixel.material !== brick.material ? 0.15 : 0;
  return labDist + materialPenalty;
}

Spatial Stabilization

Quantized images have a speckle problem: isolated pixels of wrong colors create noise. I added a spatial stabilization pass that:

  1. Splits the image into 4x4 blocks
  2. Runs a coarse quantization per block with a "block anchor" weight
  3. Applies a despeckle pass that replaces isolated single-pixel colors

The Fidelity Pipeline

The full pipeline:

  1. Resize to target grid (48x48 or 64x64 studs)
  2. Convert to OKLab
  3. Quantize with material-aware matching
  4. Spatial stabilization (block anchor + despeckle)
  5. Edge preservation check
  6. Render 4-layer Canvas preview

Results

The tool is live at www.bmbrick.com. Free to try, free (during launch period) for the full parts list.

Faces look like faces. Fur looks like fur. It's not perfect, but it's a massive improvement over naive RGB matching.

Built with vanilla JS, Canvas API, and Web Workers. No framework, no bundler.

Comments (0)

Sign in to join the discussion

Be the first to comment!