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:
- Splits the image into 4x4 blocks
- Runs a coarse quantization per block with a "block anchor" weight
- Applies a despeckle pass that replaces isolated single-pixel colors
The Fidelity Pipeline
The full pipeline:
- Resize to target grid (48x48 or 64x64 studs)
- Convert to OKLab
- Quantize with material-aware matching
- Spatial stabilization (block anchor + despeckle)
- Edge preservation check
- 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.
Top comments (0)