Why Photo-to-Pixel Conversion Always Looks Dirty (and How to Fix It)
The short answer to why photo to pixel conversion looks dirty is that the conversion is usually the last step in a process that should have been the second. People load a JPEG, pick 32 colours, pick a 64x64 grid, hit convert, and get mud. The conversion itself is fine. It is doing exactly what it was told with the input it was given.
I have watched this fail in the Neta Studio 3D game builder enough times to know the order of the causes. Four of them, and they stack. Input noise, palette size, grid size chosen after the fact, and the resampling filter. Fix them in that order and the output stops looking dirty. Fix them out of order and you will spend an hour nudging a colour count slider that was never the problem.
The rest of this post is the diagnosis, the working order, and the one test that tells you whether the result actually works: squint at it.
Cause 1: Input noise gives the quantiser too many near-identical colours
A photo is not a set of colours. It is a set of colours plus grain, JPEG ringing, sensor noise, skin texture, dirt, and three blades of grass in the corner that each contain eleven distinct greens.
When you hand that to a quantiser, you are asking it to pick, say, 16 representative colours out of the possibly tens of thousands of distinct RGB values in the image. The quantiser does not know which of those values are the subject and which are noise. It clusters by frequency and distance, and grain is spread evenly across the whole image, so grain eats palette slots that should have gone to the subject.
This is the single most common cause of a dirty result and the one people skip. Symptoms: the output has a grey-green haze over a region that was clean in the photo, or the shadows turn into a speckled mix of four colours that are almost the same, or a flat wall gets two or three dithered tones where it should get one.
The fix is to simplify before you convert. Not after.
What "simplify" means concretely
- Blur the photo slightly before quantising. A small radius — enough to kill grain, not enough to blur edges you intend to keep. This is the one place where a smoothing filter helps, because it is operating on the input, not on the palette.
- Reduce the colour depth of the source first, then flatten. If your editor has a "posterize" or "cutout" filter, apply it and look at the preview. You are trying to reach a point where the image looks like a bad watercolour painting. That is the right input for a pixel conversion.
- Desaturate if the photo has colour noise in the shadows. Shadow chroma noise is the worst offender because the quantiser cannot tell it from real colour.
A concrete number: if a region of your photo that should be one flat colour shows more than a handful of distinct values when you colour-pick around it, that region will produce speckle in the output regardless of what palette size you choose. Fix the region, not the palette.
Busy backgrounds are the same problem, larger
If the subject is a character against a cluttered street, the background contributes most of the colour diversity and none of the meaning. The quantiser will happily spend twelve of your sixteen palette slots on asphalt, signage, and a parked car, leaving four for the face.
Crop or mask the subject out before conversion. This is the first item in the working order below for a reason.
Cause 2: Palette size decides whether the grid reads as a grid
Here is the failure mode. You convert at 64x64 with 64 colours. The output has 64x64 cells, but because there are enough colours, every cell gets a shade close to what the photo had at that point. The result is not pixel art. It is a low-resolution photo with grid lines. It reads as blurry, not as blocky.
Pixel art reads as pixel art because the palette is small enough that the same colour has to be reused across large runs of cells. That reuse is what creates readable shapes. Too many colours and there is no reuse, so there are no shapes, only gradients.
The relationship is roughly: fewer colours than cells, by a lot. If you have 4,096 cells (64x64), you do not want 4,096 colours or anything near it. You want a palette small enough that adjacent cells must share.
Set the palette before the grid
This is the counterintuitive part. Most tools present grid size first because grid size is the visually obvious parameter. But the palette is the constraint that decides whether the grid can be read at all.
Practical starting points, as defaults to deviate from:
Subject | Grid | Palette | Why |
|---|---|---|---|
Character portrait, head only | 32x32 | 8–12 | Enough cells for a face, few enough colours for shading to read as bands |
Full-body character | 48x48 or 64x64 | 12–16 | Needs more cells for limbs, still needs colour reuse |
Tile or prop | 16x16 | 4–6 | At this size, more colours than 6 produce noise, not detail |
Small icon | 8x8 | 2–4 | Anything more is unreadable at display size |
Background panel | 64x64 or larger | 8–16 | Large flat regions, so low colour count is safe |
These are starting points. The rule that matters: if you find yourself raising the palette to fix a readability problem, you probably have a grid problem or an input-noise problem instead.
If two colours in your palette are close enough that you cannot name the difference out loud, delete one of them. Palettes get dirty from near-duplicates, and near-duplicates are almost always a leftover from quantising a noisy input rather than a deliberate shading choice.
Cause 3: Grid size chosen after the fact is a grid size chosen wrong
The grid is a display decision, not a photo decision. It has to match where the art will end up, at what scale, on what surface.
The common mistake is deriving the grid from the photo's aspect ratio or from a sense of "how much detail do I want". Neither is the constraint. The constraint is: how many pixels will this thing occupy on screen when it is used?
If you are building in the Neta Studio 3D game builder and the sprite will be a billboarded character a few metres from the camera, the on-screen size might be a couple of hundred pixels tall. A 64x64 sprite scaled to that size gives you chunky, readable blocks. A 256x256 sprite scaled to the same size gives you mush, because the conversion spent detail budget on cells that get averaged away.
Conversely, if the sprite is a UI element drawn at 1:1 in a corner of the screen, a 16x16 grid is too coarse and you will not be able to render the face at all.
Match the grid to the use
- Decide the final on-screen pixel size first.
- Decide how many source cells you want per on-screen block. Two to four is a typical range for readable pixel art at scale.
- Divide. That is your grid.
If the sprite renders at roughly 128 pixels tall and you want each source cell to occupy about 4 screen pixels, you want a 32-cell-tall grid. Not 64, not 128. The photo's proportions are irrelevant; you derive the other dimension from the aspect ratio of the crop, not from the original image.
There is a second, related failure here. People convert at a large grid and then downscale the resulting pixel art to the size they actually need. This destroys the hard edges that made it pixel art. Convert at the target size, not above it.
Cause 4: Resampling with a smoothing filter destroys a hard-edged palette
This is the one that produces the specific "dirty" look more than any other single cause, and it is the easiest to fix once you know it exists.
Any resampling filter that interpolates — bilinear, bicubic, Lanczos, and the various "smooth" and "adaptive" options — computes each output pixel as a weighted average of several input pixels. Those averages are new colours. They did not exist in your source image and they are not in your palette.
So the pipeline goes: quantise to 12 colours, then resample with a smoothing filter, and now you have 12 colours plus a few thousand intermediate shades along every edge. The edges look fuzzy and slightly wrong-coloured. The palette is no longer enforced. That is the dirt.
Set the downscale resampling to nearest-neighbour. Every serious image tool has this option. It is sometimes called "point" or "none". It samples one input pixel per output pixel and copies its value. No new colours are created.
Order of operations matters here too
Nearest-neighbour must be the filter for the downscale that happens as part of the conversion. If your tool does the downscale internally, find its resampling setting and set it. If you are doing it manually, do the downscale with nearest-neighbour before quantising, not after.
If your conversion tool does not expose a resampling setting, you can test what it is doing: convert a photo that contains a hard black-to-white edge. If the output edge has grey cells between the black and the white, the tool is interpolating and you cannot get a clean palette out of it. Do the downscale in an external editor and feed the tool an image that is already at grid size.
The working order
Do these in this sequence. Skipping ahead is what produces the dirty result, because each step depends on the one before it being settled.
- Crop to the subject. Remove background clutter, framing, and anything that is not the thing you are converting. This reduces colour diversity before anything else touches the image.
- Reduce noise. Light blur to kill grain, then desaturate shadow noise, then flatten with a posterize-style filter. Target: the image should look like bad flat-colour art with hard bands, not like a photo.
- Choose the grid. Derive it from the final on-screen size and the source-cells-per-screen-block ratio. Not from the photo's dimensions.
- Choose the palette. Small. Start lower than you think. Fewer colours than cells by a wide margin. Remove near-duplicates.
- Convert, with nearest-neighbour resampling. No smoothing filters anywhere in the downscale path.
Note that the crop comes first and the palette comes fourth. Most people do the opposite: they convert first and then try to fix the result. Once you have a 12-colour quantised image with interpolated edges, you cannot recover the input information you threw away.
A note on iterating
When the result is wrong, change one variable at a time and re-convert. Changing grid and palette together means you cannot tell which one fixed it, and you will not be able to reproduce the good result on the next asset.
The variable most worth re-testing is the input. Go back to step 2 and simplify harder. Nine times out of ten, a result that is still dirty after you have fixed the palette and set nearest-neighbour is dirty because the input still has noise in it.
The one test: squint at the result
Blur your eyes, or step back from the monitor, or scale the result down to thumbnail size for a second. What you are checking is whether the subject still reads.
At thumbnail scale, noise disappears and structure remains. If you can still tell what the thing is — that it is a face, a character, a specific prop — the conversion worked. If it turns into a grey smear or an unrecognisable cluster of blocks, the structure is not there, and no amount of palette tuning will add it. It means the grid is too small for the subject, or the input was too noisy for the quantiser to find the structure in the first place.
The squint test is also how you catch the opposite failure: a result that looks detailed and correct at 1:1 but collapses to mush when scaled down. That means your palette has too many near-duplicate shades and the low-contrast detail is carrying the read instead of the shapes.
Applying it in practice
Run the squint test at the size the asset will actually be used. If it lives in Mini World at a particular on-screen size, look at it at that size. If it is a sprite in The Cyclops' Island, look at it under the same camera distance.
The reason this matters is that the asset is not judged at 1:1. It is judged at whatever the player sees. A conversion that passes the squint test at the usage size is correct even if it looks crude when you zoom in. A conversion that fails the squint test at usage size is wrong even if it looks detailed when you zoom in.
This is the same principle behind Agent Sprite Forge's approach to sparse, low-count palettes: the constraint is readability at scale, and the palette is sized to serve that, not to preserve the source photograph.
Why fixing causes out of order wastes your time
The four causes are ordered by how much damage they do and by how early in the pipeline they occur.
Input noise is first because it corrupts the input that every later step reads. Palette size is second because it decides whether the grid can be read at all. Grid size is third because it is a display decision that has to be made before conversion, and it is easy to get wrong by deriving it from the photo. Resampling is fourth because it is the easiest to fix and the easiest to overlook.
If you fix resampling first and your input is noisy, you get a clean-edged muddy image. If you fix the palette first and your grid is wrong, you get a readable grid of the wrong size, which then gets downscaled and destroyed. If you fix the grid first and your input is noisy, you get a correct grid full of speckle.
The order in the working list is the order that avoids rework. Crop, denoise, grid, palette, convert.
Summary
Why photo to pixel conversion looks dirty is almost never the converter. It is one or more of four specific things: a noisy input that gave the quantiser bad choices, a palette large enough that no colour had to be reused, a grid derived from the photo instead of from the final use, and a smoothing resampling filter that invented colours the palette did not contain.
Fix them in order. Crop to the subject. Reduce noise until the image looks like flat colour bands. Choose the grid from the final on-screen size. Choose a palette smaller than you want. Convert with nearest-neighbour.
Then squint. If the subject still reads at the size it will actually be used, you are done.
Keep going
- the photo-to-pixel converter
- the pixel art size guide
- the pixel art maker
- the AI pixel art generator
- the pixel art tool index
- the Aseprite alternatives
Build the world it walks in
A character, a tile or a model is a material with nowhere to stand. Every world on the 3D game builder was made from one written description and published as a page you can play — including Mini World and The Cyclops' Island.