Unfake Pixel Art (AI to Real)
Your AI 'pixel art' is fake. This node makes it real
- image
- pixel_art_image
- manifest
Every pixel-art model on Civitai has the same dirty secret: it doesn't output pixel art. It outputs an image that looks like pixel art from a distance - off-grid, blurry, and carrying hundreds of near-duplicate colors from compression and hallucinated shading. Squint and it's fine. Zoom in, or try to drop it into a game engine, and the grid falls apart. Unfake Pixel Art (AI to Real) is the fix: a postprocessing node that detects the real pseudo-pixel grid hiding in that mush, snaps it into true alignment, and quantizes it down to an honest, retro palette.
This is a ComfyUI port of jenissimo's unfake.js, which blew up on r/StableDiffusion in mid-2025 (+600 votes, 116 comments) for a reason - the demo genuinely makes AI garbage into usable sprites. Tauraloke's ComfyUI-Unfake-Pixels did the first rough port; this pack (CalaKuad1) is the more complete rewrite, adding background removal, exact target sizing, and an in-node preview. No model files, no API, no key - it's pure math running on CPU.
How it works
The node runs a fixed pipeline, and understanding the order explains most of your troubleshooting:
- Background removal (if enabled) - takes the most common corner color, and using connected components deletes only the exterior region, not matching colors trapped inside the subject. It's heuristic, not a BiRefNet-style model, so it's free and instant but blunt.
- Scale detection - the clever part. It splits the image into tiles, runs a Sobel edge profile on the most informative ones, and finds the repeating peak distance. That spacing is your pixel size, even when the grid is blurry or upscaled.
- Optimal crop - shifts the crop by the smallest offset that lands every pixel on the detected grid, killing half-pixels.
- Downscale + quantize - reduces the image by the detected scale, then runs K-Means clustering to cap the palette (default 16 colors).
- Jaggy cleanup - replaces isolated noise pixels with their neighbors' average for cleaner lines.
The inputs that matter
You'll touch maybe four of these. The rest have sane defaults.
- target_width / target_height (default 64 each) - set both to your final resolution and the node computes scale, crops, and pads to hit it exactly. Set both to 0 to keep the auto-detected original aspect. Gotcha from the source: it's all-or-nothing - one zero and the pair stops forcing the target.
- manual_scale_factor - override when auto-detection botches it. If your input is 1024 and you want 16, that's a scale of 64. The README is honest that noisy textures like dirt and ores defeat the auto-detector.
- remove_background + background_tolerance - turn this off if your subject touches a corner, or the "background" color shows up inside the art.
- max_colors (2–256) - palette cap; 8–16 is authentic retro, 32+ drifts toward "AI mush" again.
- downscale_method -
nearestis crisp and punchy;dominantaverages each block's most common color, which is gentler on gradients.
The ea_* knobs (ea_tile_grid_size, ea_min_peak_distance, ea_peak_prominence_factor) tune the edge detection. Leave them alone until you've confirmed a bad detection.
Outputs
pixel_art_image (IMAGE) - your cleaned sprite. Wire it to a Save Image or preview. The node also renders an 8x nearest-neighbor preview (the upscale_preview_factor input) inside the UI so it doesn't look 16×16-tiny in the graph.
manifest (STRING) - a dictionary string with detected_scale, crop_offset, and processing_time_ms. This is your diagnostic: if detected_scale is 1, detection failed and the node bailed to "no downscale," which is why your output still looks blurry.
Installation
ComfyUI Manager is easiest - search "PixelArt Unfaker." Or manually:
cd ComfyUI/custom_nodes
git clone https://github.com/CalaKuad1/ComfyUI-PixelArt-Unfaker
cd ComfyUI-PixelArt-Unfaker
pip install -r requirements.txt
Then restart. The dependency that matters: requirements.txt pulls in scikit-learn and scipy on top of the torch/Pillow/numpy ComfyUI already ships. If the node throws an import error, that's the missing piece. On the portable Windows build use .\python_embeded\python.exe -m pip install -r requirements.txt.
Where people get burned
Auto-detection failing is the big one - check the manifest's detected_scale and reach for manual_scale_factor. Feed it a sprite that's already clean and it can over-quantize into posterized blocks, so set max_colors honestly. And remember the background removal is corner-color logic: on busy scenes just switch it off and do transparency in a proper removal pass later. For a node this niche, it does the one job - turning fake pixel art into something a game engine will actually accept - remarkably well.
Inputs (14)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| target_width | INT | 640–4096 | Ancho exacto final (ej. 64). 0 para mantener original. |
| target_height | INT | 640–4096 | Alto exacto final (ej. 64). 0 para mantener original. |
| manual_scale_factor | INT | 00–128 | Fuerza la escala (ej. 16 para 1024->64). Ignorado si target_width > 0. |
| remove_background | BOOLEAN | true | — |
| background_tolerance | INT | 100–255 | Tolerancia de color para quitar el fondo. |
| max_colors | INT | 162–256 | — |
| cleanup_jaggies | BOOLEAN | true | — |
| downscale_method | COMBO | nearest | 2 options: nearest, dominant |
| scale_detection_method | COMBO | edge_aware | 1 options: edge_aware |
| ea_tile_grid_size | INT | 31–10 | — |
| ea_min_peak_distance | INT | 51–50 | — |
| ea_peak_prominence_factor | FLOAT | 0.100.01–1 | — |
| upscale_preview_factor | INT | 81–32 | Escala la imagen de salida para la previsualización |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| pixel_art_image | IMAGE | — |
| manifest | STRING | — |