Fix AI Pixel Art — Snap Generated Art to a Real Pixel Grid
Images from image generators look like pixel art until you zoom in: the "pixels" are uneven blocks with noisy colours and soft edges. The Studio's Cleanup panel measures the block size, rebuilds one clean pixel per block and limits the colours — and tells you plainly when it cannot find a trustworthy grid.
Images from image generators imitate pixel art but are not drawn on a grid: the “pixels” are blocks of uneven width with colour noise and soft edges. A fix needs one cell size, one colour per cell and a smaller palette — and for many generated images no single cell size fits the whole picture. Nerulio’s Cleanup panel measures first and applies only a grid it trusts; all 7 real generated images we tested were judged “unsure” and nothing was applied automatically. It shows its best candidate so you can type a size and check the result yourself.

- Palette filesMeasured· read back
- .asepriteVerified· Aseprite 1.3.18
- Engine bundlesVerified· Godot 4.7.2 · Unity 6000.5
- Frame PNG · .nerulioPlain files
Why generated “pixel art” has no grid to snap to
Real pixel art is drawn on a grid, so an upscale of it has one block size everywhere. An image generator paints at its own resolution and only imitates the look: blocks drift in width (our simulation uses 80–120 % of the nominal size), neighbouring “pixels” differ by a few colour levels, edges are soft, and the file is often JPEG-compressed or carries a drawn background grid.
A snapping tool has to choose one cell size. When widths drift, a regular lattice fits one part of the picture and cuts through the middle of blocks elsewhere. Nerulio tries a lattice first; for noisy pictures whose edges sit at least 0.1 cell off it, it follows the edges instead — cells between 0.7 and 1.35 of the typical size, the largest size whose cuts land on at least 97 % of the edge energy — but only when that tracked cell is at least 3 px and roughly square.
When none of that holds, the panel says “A grid of ≈ … px was found, but it is too weak to trust” and applies nothing. That happened to all seven real generated images we tested. On the 630 × 500 previews Nerulio’s weak candidate was about 3.3 px; for one of them unfake.js output 209 × 166 (a size of 3) and Pixel Snapper 106 × 84 (about 6): the tools disagree by a factor of two, which is exactly why a guess should not be applied silently.
- Pseudo-pixel
- A block that looks like one pixel but differs in width, colour and edge sharpness from its neighbours.
- Mixels
- Pixels of different sizes in one picture; generated images have them by construction.
- sure · likely · unsure
- The confidence shown by the Cleanup panel. Only sure and likely grids are applied without you typing a size.
What the benchmark measured on generated art
Ten simulated cases have a known 1× original (cells of 5–10 px, widths 80–120 %, colour drift and noise, some blur or JPEG); the seven real images have none:
simulated generated art (10 cases)
size exactly right 20 % within 1 px 80 %
pixels exact 36.1 % (defaults) 70.3 % (background kept)
pixels within ΔE 0.02 92.8 % (background kept)
real generated images (7, no truth)
Nerulio 7 of 7 “unsure”: size left unchanged, candidate shown
others always return a guess, e.g. 630 × 500 → 209 × 166 (unfake.js) or 106 × 84 (Pixel Snapper)Being within 1 px of the right size is not enough for exact pixels: one column too many shifts every cut after it. That is why the simulated cases reach 92.8 % of pixels within a small colour distance but only 70.3 % exact.
What it does
- Edge tracking follows pseudo-pixels of uneven width: significant edges above the noise floor, the largest cell size whose cuts land on at least 97 % of the edge energy, and flat runs divided evenly. It is only used when the colours are noisy and the evidence agrees.
- On simulated generated art with known originals it found the size within 1 px in 80 % of cases (exactly: 20 %), and with the background kept 70.3 % of pixels came out exact and 92.8 % within ΔE 0.02.
- Seven real generated images were all judged unsure and left at their size, with the best candidate shown (about 3.3 px on one set of previews) so you can type it and apply it yourself.
- Colour merge in Oklab, a colour limit, quantizing to the sprite palette, anti-alias fringe and stray-pixel removal, then a new sprite you can fix by hand with the pencil.
How to do it
- Drop the generated image on this page; it opens in the Pixel workspace.
- Press Measure in the Cleanup panel and read the verdict and its confidence.
- If it says unsure, zoom in on the grid, type the pixel size you see and set a colour limit.
- Preview, Apply as a new sprite, and retouch the pixels that still look wrong.
Check the result
- Read the confidence first: a tracked grid (“Uneven pseudo-pixels ≈ 6×6 px”, likely) is applied; “too weak to trust” is not.
- If you type a size, turn on “Show the grid on the canvas” and zoom in: cuts should run between blocks across the whole picture, not only in one corner.
- Compare before and after in Preview at 2× or 4×: eyes, outlines and highlights should survive as single pixels.
- Check the colour count in the report and set a colour limit (16–32) if it stays high.
Troubleshooting
| Symptom | Likely cause | How to check | Fix |
|---|---|---|---|
| “A grid of ≈ 3.3 px was found, but it is too weak to trust” | Block widths drift, or JPEG noise and a drawn background grid hide the real cell size | Zoom to 800 % and measure a few blocks: do they differ by a pixel or more? | Type the size you measure; if blocks differ too much, clean a cropped region separately or redraw |
| Result looks like a smaller, blurrier copy | The typed size is half the real block, so each block became 2 × 2 mixed pixels | Two cuts inside one block on the canvas grid | Type the double size |
| Eyes or one-pixel highlights vanished | The typed size is too large, or colour merging removed them | Preview; the report lists merges and fixes | Smaller size, Merge colours Light or Off, keep stray-pixel removal off (its default) |
| Still hundreds of colours | Generated colour noise inside each block | Report: colour count and “noisy” | Colour limit 16–32, or quantize to a palette (Lospec palettes) |
| A background or checkerboard pattern remains | The generator painted a background or a fake transparency pattern | Report: detected background and its share of the border | Background removal takes one solid border colour; select a checkerboard with the magic wand and delete it |
Other ways to do it
| Option | When it is the better choice |
|---|---|
| Redraw it at 1× in the pixel editor | The generated image is a concept: tracing it on a real grid gives cleaner art than any snapping, and you choose the resolution. |
| Pixel Snapper, unfake.js or perfectPixel | You want a result for every image without a confidence check, or a command line or library for batches. On simulated generated art perfectPixel found the exact size more often (40 % against 20 %). |
| Pixel Lab after snapping | Several frames of one generated animation need one shared palette and anti-alias cleanup. |
Engine exports and how each was checked
"Verified" means the exported files were loaded by the real engine or tool on real CC0 assets and the result was compared with the source. Anything that was not loaded is labelled as such.
| Target | Files | Check |
|---|---|---|
| .aseprite | layers · blend · opacity · locked · indexed palette · frames · tags | Verified Aseprite 1.3.18The real Aseprite command line reopened the file as INDEXED with 2 layers, Multiply at opacity 160 and the locked layer not editable; the frames are pixel-identical. |
| Palette files | .gpl · .pal · .hex · .ase · .act · .json | Measured read backEach format was written and read back identical; Aseprite's own .gpl, .pal, .hex and .act and a real Adobe .ase (16 colours) were read. |
| Engine bundles | Godot 4 · Unity 6 · Phaser 3/4 · PixiJS 8 … through Pack & Export | Verified Godot 4.7.2 · Unity 6000.5The sprite you drew is packed in Pack & Export; those engine files were loaded in each engine on real CC0 assets (47 of 49 runs pass; the 2 failures are a Phaser 3.90 bug the Phaser 3 preset avoids). |
| Frame PNG · .nerulio | PNG-8 with the palette when an indexed frame fits it · project file | Plain filesPlain files for any tool; no engine run is claimed for them. |
Evidence
Cleanup benchmark, 69 CC0 cases with a known 1× original: the exact size found for 81 % (±1 px: 90 %) and 74.0 % of pixels exact with the background kept; the best competitor found 30 % of sizes (perfectPixel) and the best exact-pixel score was 44.6 % (unfake.js). An Aseprite-made ×4 nearest upscale came back 100 % exact and a ×3.78 bilinear resize 99.7 %, both with the size found automatically. A 1 px outline and a drop shadow matched Aseprite pixel for pixel (1846 and 705 px). .aseprite files written here reopen in Aseprite 1.3.18 as indexed, with their layers, blend mode and lock.
Limits
- No generated image in our tests was snapped automatically: expect to type the size.
- Nothing is redrawn: missing detail stays missing and uneven shapes stay uneven at 1×.
- The real images have no ground truth, so how good a typed size is can only be judged by eye.
- Not in the Pixel workspace (Aseprite has them): layer groups, linked cels, tilemap layers, reference layers, RotSprite and free rotation or scaling of a selection (only flips and 90°), gradient, spray, contour and text tools, custom brushes (square or round, 1–16 px), tiled drawing, Lua scripting and a command line.
- The symmetry axis is typed, not dragged on the canvas. On touch there is no right button, so the background colour needs the swap button or X.
- Cleanup: JPEG input comes back close (84 % of pixels within ΔE 0.02 with the background kept) but rarely exact (12 %); simulated generated art gets the exact size 20 % of the time (±1 px: 80 %); none of 7 real generated images was snapped automatically — you type the pixel size the panel suggests.
- Indexed conversion picks the nearest colour in Oklab, not Aseprite's RGB distance: 68 % of the test sprite's pixels got a different (perceptually closer) pick. The Lospec search is the only network request and asks first.
Versions and sources
Benchmark 2026-09-25 (docs/STUDIO-PIXEL.md §5, docs/pixel-bench/H2H.md): 10 simulated cases with truth and 7 real CC0 generated images from OpenGameArt; competitors run with their own defaults.
Questions
Does it fix "mixels" (pixels of different sizes)?
Often, not always. Uneven pseudo-pixels are what edge tracking is for, and on simulated generated art the size came out within 1 px in 80 % of cases. When the evidence is weak the panel says unsure and changes nothing until you type a size.
Is anything generated or redrawn?
No. Each output pixel is the most common colour of its cell (or its Oklab medoid), and the palette comes from the picture or from a palette you choose. The original image is never modified; the result is a new sprite.
What about palette locking and anti-alias removal across many frames?
The Cleanup panel has a colour limit and palette quantizing. The classic Pixel Lab also locks several frames to one palette and snaps anti-aliased edges; its page is linked below.
Is it free, and do I need an account?
Everything on these pages is free and works without an account. There is no watermark and no export that is held back.
Are my sprites uploaded?
No. The Studio decodes, packs and exports in this browser tab. Projects are autosaved in the browser (IndexedDB) and can be saved as a .nerulio file.
Does it need Aseprite or an engine plugin installed?
No. .aseprite files are read and written in the browser, and each engine bundle carries what that engine needs (for Godot, the import settings and scene; for Unity and the Godot TileSet, a small editor script).