Pixel Grid: Autofixer
The node that makes diffusion's fake pixel art into actual pixel art
- image
- fixed_image
- hex_palette
- pixel_size_used
You got something that reads as pixel art from across the room. Zoom in and the illusion dies: three different pixel sizes, 300+ colours, soft edges, a stray speck of teal on the cheek. Not your prompt's fault - diffusion decodes from a continuous latent space and can't land on a grid, and no pixel-art checkpoint fixes that. The community's answer is settled: generate the composition, then run a deterministic pass over it.
Autofixer is that pass: grid detection, integer downscale with dominant-colour voting (not averaging, or you get mush), palette quantization, and nearest-neighbour upscale back to display size - all four in one node, inside your graph instead of in an external tool between generations.
How it works
Finding the grid. It looks for a block size where the image's strong edges (>48/255 between neighbours) pile up on the same boundaries - a candidate must divide both width and height evenly and land ≥98.5% of its transitions on its own boundaries. It tries 2 through 24, keeps the largest that qualifies, and falls back to 1 if nothing does.
Turning each block into one pixel. This is the sampling enum, the setting most people never touch. Inside every block it finds the dominant colour family (colours quantized to 5 bits per channel, most common wins), then picks the family member closest to the block's centre. Ink-preserving, the default, adds one rule: if ≥20% of the block's pixels are genuinely dark and 35 levels darker than the winner, use the darkest near-centre pixel instead. That's how a one-pixel outline survives a block whose average colour is the fill. Exact pixels takes the centre pixel, Crisp shapes skips the ink pass, Area coverage averages the block.
Cutting the palette. Weighted median-cut over the logical pixels, then every pixel goes to its nearest entry. The default 12 is enough for shading and low enough to read as period-correct.
Saving the eyes. Before quantizing, it scans for small colour components (≤2.5% of the frame) whose colour the base palette would badly misrepresent, ranks them, and reserves palette slots for them - up to 3 accents at 12 colours, up to 8 at 256. Those pixels are protected from cleanup, which is why a 12-colour result can keep an eye colour and a gold earring.
Removing specks. Only components of three pixels or fewer, away from the border, whose neighbours agree ≥72% on one colour within 0.065 perceptual distance get recoloured. Four-pixel islands and border specks survive - that's by design, and harder cleanup is the pack's Median Fixer, not a setting here. The result is integer-replicated back to your input dimensions and cropped to fit, so fixed_image matches what you fed in with no smoothing.
The inputs that matter
image is the only required input. Of the optional four, three matter:
pixel_size-0detects automatically,1means already native resolution, anything higher is the block size you know (e.g.6).colors- 2 to 256, default12 colors - recommended. Go to 16 or 24 if shading disappears.palette- optional, and it overridescolorsentirely. Strict format: comma-separated six-digit#RRGGBB, max 256 entries. Feed it a reference sprite's palette and the whole image snaps to it. Outputs:fixed_imagegoes to Preview or Save.hex_palette(STRING) wires into the pack's Enforce Palette, Palette to Image, or HEX to Color Names.pixel_size_used(INT) is your window into the detector - check it first when a result looks off.
Install
ComfyUI Manager → search Pixel Grid Helpers → install → restart. Or:
cd ComfyUI/custom_nodes
git clone https://github.com/molbal/ComfyUI-PixelGridHelpers.git
Or comfy node install comfyui-pixelgrid-helpers from the ComfyUI root. No heavy dependencies: the pack's pyproject.toml declares an empty dependency list and only needs torch, which ComfyUI already has. It's MIT, version 1.1.0, from October 2026 - a young one-author pack, with its nodes under Pixel Grid Helpers.
Where it goes wrong
Detection silently gives up. The most common surprise is a result that's cleaner but still off-grid. Check pixel_size_used: if it reads 1, the detector found nothing - dimensions not divisible by the block size, a block larger than 24, or too few usable boundaries. A sprite scaled by 3.5x has no grid to find at all; resize to a divisible size first, or set pixel_size by hand.
A palette string kills the run. Anything that isn't a bare six-digit hex throws Invalid palette color and the node errors out - #F00 won't work here, even though this pack's HEX to Color Names node accepts three-digit shorthand.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| pixel_sizeopt | INT | 00–128 | 0 detects enlarged pixel blocks automatically; 1 keeps the native grid. |
| paletteopt | STRING | Optional comma-separated #RRGGBB palette. | |
| samplingopt | COMBO | Ink-preserving - thin dark lines | How each source pixel block becomes one logical pixel. |
| colorsopt | COMBO | 12 colors - recommended | Maximum generated palette size. A custom palette overrides this setting. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| fixed_image | IMAGE | — |
| hex_palette | STRING | — |
| pixel_size_used | INT | — |