Free pixel art palette extractor
Pixel art color palette, read out of the picture
Drop a sprite in and this page lists every distinct color the file holds — counted over every one of its pixels, unbucketed, with an exact pixel count and share against each — then chains them into the shading ramps a pixel artist actually works from and measures how far each ramp drifts in hue. Free, unregistered, and the file is opened by this page and never sent from it. Past 512 distinct colors a list stops being a palette, so the page says so rather than printing it, and offers to reduce the picture to between 2 and 64 instead.
- 100% free
- No signup
- Exact, unbucketed count
- Grouped into ramps
- GPL, PAL, HEX, CSS
Press Ctrl+V anywhere on the page to drop in a frame lifted straight out of a game window — no need to click the field first.
Exact and unbucketed, over every pixel rather than a sample. Offered up to 512 colors, which is past the largest palette anyone hand-authors.
It will not merge two colors that differ by one in a single channel. Those are separate entries because the file says they are, and deciding they are the same shade is the other mode’s job.
How far apart two shades can sit and still belong to one ramp. Measured in Oklab, but a swerve in hue is charged 3 times what a climb in lightness is: without that, a dark navy and a dark red come out closer together than two shades of the same blue. Below about 0.24 a hue-shifted five-shade ramp breaks into pairs; above about 0.32 white starts joining the gray chain. Grouping is a reading of the palette, not a fact about it — the colors and their counts do not change when you move this.
How to read a palette out of a sprite
Nearly every wrong answer here comes from feeding it a re-saved copy instead of the file the editor wrote.
Give it the file the editor wrote, not a copy of it
Drop or paste the PNG or GIF at the size it was exported, because the count is a fact about the file rather than about the artwork. A JPEG of a sprite comes back with thousands of near-duplicates, a 4x upscale that was later scaled down comes back with blended edges, and a screenshot of the canvas brings the editor's transparency checkerboard in with it. All three are counted honestly, which is the problem: the honest count of the wrong file is not your palette.
Read the number before you read the colors
The heading gives the distinct color count and the ramp count, and that first number is the check. If a sprite you drew with sixteen colors reports sixteen, the file is clean and everything below it is exact. If it reports four hundred, something between your editor and this page added colors, and the note under the table names the likely cause. Past 512 the page refuses outright rather than printing a histogram and calling it a palette.
Set the ramp step, then take the format your editor reads
The ramps are grouped at a step of 0.28 by default: lower it toward 0.18 if a four-shade ramp should have been two, raise it toward 0.40 if the skin and the wood ended up in the same chain. Moving it regroups the swatches and changes nothing about the colors or the counts, because the grouping is a reading of the palette rather than a fact about it. Then save it — .gpl opens in Aseprite, GIMP, Krita and Inkscape, .pal in Pro Motion and GrafX2, and the hex list pastes into almost anything else.
Technical specifications
| Colors counted | Every distinct RGB value in the file, with no bucketing and no rounding, over all of its pixels rather than a sample — a 64 x 64 sprite is read at all 4,096 of them |
|---|---|
| Where an exact list stops | 512 distinct colors. Past that a list is a histogram rather than a palette, so the page refuses instead of truncating; the fallback reduces the picture to between 2 and 64 by median cut in Oklab with the relaxation pass off |
| Decode ceiling | 2,048 pixels on the long side and 25 MB on disk. The limit is a power of two so that artwork saved at 512, 1,024 or 2,048 px is counted exactly as it was written; past it a picture is scaled to fit, which blends neighboring pixels into colors the artwork never held, and the result says so when a side comes back at exactly 2,048 |
| Semi-transparent pixels | Skipped below 128 of 255 alpha, so a transparent background contributes nothing to the list or to the shares. At or above that threshold a pixel is counted at its stored RGB, which is why a feathered eraser adds entries and a hard one does not |
| Ramp grouping | Chained by lightness in Oklab, with a swerve in hue charged 3 times what a climb in lightness is: a shade joins the ramp below it when that weighted step is under the threshold, default 0.28 and adjustable from 0.18 to 0.40. Colors under 0.03 chroma are chained separately as neutrals, so a gray cannot weld two materials together |
| Reported for each ramp | Shade count, the lightness climbed from the darkest shade to the lightest in Oklab L, and the signed hue rotation across the ramp in degrees — which is hue shifting stated as a number instead of as advice |
| Palette formats written | GIMP and Aseprite .gpl in ramp order with Columns set to the longest ramp, JASC .pal, a lowercase hex list, CSS custom properties, CSV with channels, ramp position, pixels and share, and a PNG of one 32-pixel swatch per color with nothing else in the file |
| Requests made while you work | None. The picture is decoded by this tab, the counting runs in JavaScript on your own machine, and no part of the file or the palette is transmitted anywhere |
Frequently asked questions
Why does my 16-color sprite report thousands of colors?
Because the file is not what your editor wrote, and three things cause it. JPEG is the commonest: it stores color lossily, so a flat area comes back as a dozen values that differ by one or two and every edge comes back as a gradient. The second is scaling — a sprite posted at 4x and saved back down, or any picture over 2,048 pixels on the long side, gets its neighboring pixels blended into colors nobody drew. The third is a soft brush or a feathered eraser, which leaves semi-transparent edge pixels that are counted at whatever RGB is stored under them. Export a PNG at native size from the original document and the count will match what you drew.
Does a transparent background count as a color?
No — any pixel under 128 of 255 alpha is skipped entirely, so a sprite on transparency contributes only its visible pixels and the shares are computed against those. The threshold is a real edge case rather than a formality: a pixel at alpha 130 is counted at its stored RGB even though you can barely see it, which is why an anti-aliased outline adds entries and a hard one-pixel outline adds none. If you want the background counted, flatten it onto a color first and the flattened color becomes a normal entry.
What is a ramp, and why group by lightness rather than by hue?
A ramp is one material's shading sequence — the three to five shades that make up the skin, or the cloth, or the metal — and it is grouped by lightness because a well-drawn ramp does not hold its hue. Hue shifting is the technique that separates drawn pixel art from a lightness slider: shadows are swung toward blue and purple, highlights toward yellow and orange, so a single ramp can rotate thirty degrees or more between its darkest and lightest shade, and grouping by hue would cut exactly those ramps in half. What makes the chaining work is that a swerve in hue is charged three times what a climb in lightness is. Measured on real palettes, a genuine step between two shades of one material runs 0.15 to 0.22 in plain Oklab distance while a dark navy and a dark red sit 0.124 apart, so those two ranges overlap and no plain threshold can separate them — the a and b axes are compressed at low lightness, which makes every shadow look adjacent to every other shadow. Weighting the swerve pulls them apart, and the result was checked against palettes whose structure is already known: CGA comes back as each hue paired with its own bright variant, which is how the hardware defines it.
Can I load the result straight into Aseprite?
Yes, through either the .gpl file or the PNG. The .gpl is the GIMP palette format, which Aseprite, Krita and Inkscape also read, and it is written in ramp order with the Columns field set to the longest ramp so the entries land in something like their shading rows. The PNG is the other route, because those editors import a palette from an image by collecting every distinct color in it — which is exactly why the PNG here is one row of plain swatches with no gaps, no border and no caption. A black caption in the corner would arrive in your palette as a black entry.
Why are the reduced colors not the ones in my picture?
Because median cut returns the pixel-weighted mean of each group it makes, and a mean of two colors an artist chose is usually a third color that is nowhere in the file. The reduction here already leaves the relaxation pass switched off, which stops the entries sliding further toward the center of their clusters, but that is not the same as guaranteeing they exist. The checkbox is: with it on, every derived entry moves to the nearest of the 20,000 commonest colors the file actually holds, and no two entries are allowed to take the same one. Turn it off when you want the representative color rather than a real one.
How many colors should a pixel art palette have?
Between 8 and 32 for most work, and it is the ramps that decide it rather than the total. A character with skin, hair, cloth and one metal needs four ramps, and a ramp reads as a ramp at three shades and stops improving much past five — so four times four plus a couple of neutrals is 18, and that is why so many well-known palettes sit near 16. Jam limits are usually 4, 8, 16 or 32 for the same reason. A count far above that is not richer, it is usually a picture that was never quantized in the first place.
How is this different from reducing the colors in an image?
The deliverable is different: this page hands back the palette, and the reduce page hands back the picture. Both call the same median cut in Oklab from the same module, so a photograph reduced to 24 on either page yields the same 24 colors — but here they come out as a .gpl file, a hex list and a set of ramps with counts, and there they come out as a PNG of your picture painted in them. Use this one when the colors are what you are taking away; use that one when the image is.
About ramps, and why a list of colors is not yet a palette
Almost every palette extractor answers the wrong question. It sorts the colors by how much of the picture they cover and hands back a bar chart, which tells you that the sprite is 41% background and nothing at all about how it was painted. A pixel art palette is not a set of colors; it is a set of ramps. One ramp is one material’s shading sequence — the three to five shades that make up the skin, or the leather, or the blade — and an artist reaches for a ramp, never for an isolated swatch. That is why the swatches here are chained by lightness rather than listed by frequency, and why each chain reports the two numbers that describe it: how far it climbed in lightness, and how far it rotated in hue on the way. The rotation is the part worth knowing. Shading by simply darkening one color produces the muddy, plastic look that separates a first sprite from a tenth, and the fix — universal in the craft, rarely written down as a measurement — is to swing the shadows toward blue or purple and the highlights toward yellow or orange as the ramp climbs. That is hue shifting, it can turn a ramp thirty degrees or more between its darkest and lightest shade, and it is exactly why grouping colors by hue cuts the best-drawn ramps in half while leaving the flat ones intact. Feed this page a palette you admire and the degrees it reports are the artist’s technique, stated as a number you can copy.
The other half of the job is the count, and the count is wrong far more often than anyone expects, because it is a fact about the file rather than about the artwork. An indexed PNG or a GIF can address 256 colors and no more, so a file claiming 4,000 was saved as truecolor, and if it was saved as a JPEG the compression invented most of them. Scaling does the same damage more quietly: a sprite posted at 4x and saved back down arrives with every edge blended, which is the case the pixel art upscaler exists to prevent and the resizer exists to undo. This page refuses past 512 distinct colors instead of truncating the list, because a truncated histogram looks exactly like a palette and is not one — and where a picture genuinely has too many colors, reducing it is a decision that deserves its own controls rather than a silent cut.
Once you have the list, the neighboring pages do the opposite operation. Reducing colors in an image runs the same median cut from the same module but hands back your picture painted in the result rather than the palette itself; the pixel art converter takes a list you build or paste and reports what the picture did with each entry; and the 8-bit converter ignores your palette entirely in favor of the ones a console shipped with. If the colors have to become objects you buy rather than pixels you draw, the Perler bead page snaps the same picture to a manufacturer’s catalog and counts the beads.
Product and company names shown here are the trademarks of their respective owners. Photo2Pattern is an independent tool and is not affiliated with, sponsored by or endorsed by any of them.
Where the colors are counted
Your picture is opened by JavaScript running in the tab you are reading this in. It is never uploaded, never sent to a server and never stored anywhere — there is no upload step to undo, because there is no upload. Close the tab and nothing of it remains.
Every export format is written as text by this page and passed to the browser’s save dialog — no server assembles a .gpl for you and no temporary copy is kept anywhere. Nothing about the sprite survives closing the tab, including the list of hex values, so copy or save it before you go.