How to convert an image to a Game Boy palette
Four shades of green is the smallest palette anyone seriously targets, which makes it the case where every shortcut shows. Get it right here and every larger palette is easier.
1. The four colours
The original Game Boy's screen shows four levels, and the values usually cited for them are:
#9BBC0F lightest#8BAC0F#306230#0F380F darkest
They are not neutral greys tinted green. The two light shades are yellow-green and the two dark ones are close to black, so the palette has a large gap in the middle and almost nothing in the upper-mid range. Everything awkward about converting to it follows from that gap.
The hardware never stored these numbers — it drove a monochrome LCD, and the green is the screen, not the data. Every set of hex values for it is somebody's measurement of a physical panel, which is why you will find several that differ by a few points. Pick one and stay with it: mixing two across an asset pack gives you eight colours, not four.
2. Look at how many colours you are starting with
Before changing anything, check what the image actually contains. A sprite drawn in Aseprite might have eleven colours; a screenshot has thousands; a JPEG of a sprite has thousands even though it looks like it has eleven, because the compression invented the rest.
That number decides how much work the conversion is doing. Going from eleven deliberate colours to four is a reduction you can predict and hand-check. Going from forty thousand to four is a wholesale re-render, and the settings below are what make it survivable.
If the source is a JPEG, expect trouble around every hard edge: those halos are real pixels with real colours, and they will get sorted into shades along with everything else. Start from the PNG if one exists.
3. The setting that decides whether it reads
Every converter has to answer the same question for each pixel: which of the four shades is this closest to? The obvious answer — treat red, green and blue as three axes and take the straight-line distance — is what most tools do, and on a four-colour palette it is visibly wrong.
sRGB channel values are not proportional to brightness. Green carries most of what the eye reads as light, and blue almost none. So a mid green like #005500 has small numbers, gets called dark, and is sent to #0F380F — the near-black. It does not look near-black to anyone. A mid blue of the same numeric magnitude looks much darker than the green, and by raw distance the two are treated identically.
On a sixteen-colour palette this mostly washes out, because there is usually a nearby shade either way. On four shades with a hole in the middle, each wrong decision moves a pixel a long way, and areas that should separate collapse into one mass. That is what people mean when a conversion “goes muddy”.
The fix is to measure distance in a perceptually uniform space — OKLab is the current sensible default — where equal numeric steps look like equal steps. In the converter this is the by how it looks setting; switch it to by RGB numbers on the same image and the difference is not subtle.
4. Dithering: yes for photos, no for sprites
Dithering fakes intermediate shades by alternating two palette colours in a pattern, so a smooth ramp becomes a texture instead of three hard bands. On a sunset it is the difference between something that reads as a sky and something that reads as a mistake.
On pixel art it is usually wrong. Every pixel in a sprite was placed deliberately; scattering a checkerboard through a flat region replaces the artist's decision with noise, and at sprite scale the pattern is bigger relative to the art than it would be in a photograph.
If you do dither, prefer an ordered matrix — 4×4 or 8×8 — over error diffusion such as Floyd–Steinberg. Error diffusion carries the error from each pixel into its neighbours, so the pattern depends on where you started scanning: crop a sprite out of a sheet after dithering and the pattern inside it changes. Ordered dithering decides each pixel from its own coordinates, so a frame dithers the same whether it is alone or in a sheet.
5. Keeping it at four colours
A conversion that produced exactly four colours can arrive in the engine with thousands. Three places do it:
Bilinear filtering. Drawing the texture at any size other than 1:1 with filtering on blends adjacent pixels, and a blend of two shades is a fifth colour. Set the filter to nearest — Point (no filter) in Unity, Nearest in Godot's import dock, pixelArt: true in Phaser.
Block compression. DXT and its relatives work by approximating 4×4 blocks with an interpolated pair of endpoints. That is precisely the opposite of a fixed palette. Set compression to none for pixel art; a four-colour image compresses well as PNG anyway.
Scaling before export. Enlarging the image after conversion with a smooth filter reintroduces intermediate colours immediately. Scale by a whole number with nearest-neighbour sampling — the Pixel Art Resizer does this — or let the engine scale at draw time with filtering off.
Common problems
Everything collapsed into two shades
The image's mid-tones all landed on one side of the palette's middle gap. Switch colour matching to perceptual first; if it is still flat, raise the source image's contrast before converting.
The sky is three hard bands
That is what four colours does to a gradient. Turn on ordered dithering — this is the case it exists for.
The sprite came out speckled
Dithering is on and should not be. Turn it off for artwork with flat regions.
The exported PNG has more than four colours
Something scaled or filtered the image after conversion. Check the export size matches the source, and that no smoothing was applied on the way out.
Two assets use slightly different greens
They were converted against two different published DMG palettes. Export the palette once as a .gpl or .hex and convert everything against that one file.