Pixel Art Resize
The local node that actually makes pixel art
- image
- image
- small_image
- palette_json
Here's a position the community holds and holds correctly: diffusion models don't produce pixel art. They produce soft, full-color, off-grid pixels that look like pixel art from a distance and fall apart when you zoom in - inconsistent pixel sizes, stray noise, hundreds of colors. The real pixel art is made by a deterministic post-process: downscale to chunky blocks, lock the colors to a small palette, upscale back up with nearest-neighbor. That post-process is exactly what this node does, and it's the whole reason it sits in an "API Toolkit" pack: it's the one node here that runs locally, free, no API key, no network - the utility that cleans up after the API generation.
The pack name is a lie for this one in the best way. If you generate a "pixel art" image with Kling or any other model, run it through this node and it becomes actually pixelated: a real grid, a real limited palette, no blur.
The inputs that matter
Four of them do the real work:
- downscale_factor (default 8) - how much to shrink. 8 means each output pixel represents an 8×8 block of the original. Higher = chunkier, more retro.
- upscale_factor (default 8) - how much to blow it back up. Equal to downscale and the output matches the original size, just pixelated. Set to 1 for the raw small sprite.
- palette_mode - the important one.
shared_batchbuilds one palette from frame 0 and applies it to every frame - this is the setting for animations, because it stops colors from shimmering between frames.per_framegives each frame its own palette (richer, but shimmery).noneskips color reduction entirely. - max_palette_size (default 32) - how many colors you allow. The tooltip has the real guidance: 16–32 for a classic SNES look, 64+ for richer art.
- dithering -
floyd_steinbergadds error diffusion so small palettes get smoother gradients instead of banding. Slower, prettier, worth it below ~32 colors.
What comes out
image is the final upscaled pixel art; small_image is the raw downscaled version before the display upscale; palette_json is the actual locked palette as JSON - size plus a hex color list, useful if you're keeping palette consistency across separate generations.
How it actually works (briefly)
Nearest-neighbor downscale to make the blocks, palette quantization (the pack's own octree step) to lock the colors, per-pixel snapping to the nearest palette color - accelerated by a scipy KD-tree when scipy is installed, pure numpy otherwise - then nearest-neighbor upscale. Which is a reminder: the README's pip install -r requirements.txt covers it, but if you skip dependencies the KD-tree just won't be there and palette matching gets slower, not broken.
Installing and gotchas
ComfyUI Manager, search "API Toolkit", install, restart:
cd ComfyUI/custom_nodes
git clone https://github.com/IxMxAMAR/ComfyUI-API-Toolkit
cd ComfyUI-API-Toolkit
pip install -r requirements.txt
Two practical notes. First, if your image dimensions aren't evenly divisible by the downscale factor, the node uses a slightly smaller block grid and logs a notice - you'll get e.g. a 512→64 grid with a tiny 8px leftover, so size your inputs to clean multiples. Second, for a single image this is instant; for a long animation batch, shared_batch is the mode that keeps it stable frame-to-frame, which is the one setting people get wrong and then blame the node for. The tooltip says it, the source does it, and it's the difference between a coherent sprite animation and a color-flicker mess.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | Source image or batch to pixelate. | |
| downscale_factor | INT | 81–64 | How much to shrink. 8 = each output pixel represents an 8x8 block of the original. |
| upscale_factor | INT | 81–64 | How much to scale back up. Same as downscale = output matches original size but pixelated. Set to 1 for raw pixelated output. |
| palette_mode | COMBO | shared_batch | none = no color quantization. shared_batch = palette from frame 0 applied to all (best for animations, prevents color shimmer). per_frame = each frame gets its own palette. |
| max_palette_size | INT | 322–256 | Max distinct colors in palette. 16-32 for classic SNES look, 64+ for richer art. |
| ditheringopt | COMBO | none | Floyd-Steinberg adds error diffusion for smoother color gradients at small palette sizes. Slower but prettier. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| small_image | IMAGE | — |
| palette_json | STRING | — |