Pixel Palette Reduce
Palette reduction that outputs the palette
- image
- image
- palette_json
- summary_json
MKRPixelPaletteReduce is the pack's indexed-color workhorse: it reduces an image down to a small palette and returns both the quantized image and the palette itself as JSON. If you've ever stared at a noisy diffusion render and thought "this should look like a deliberate sprite," this is the node that makes it read as intentional - 16 colors and clean flat shapes will do that.
The mechanism is plain, dependency-free numpy. For adaptive mode it builds a palette by clustering the image's unique colors (a k-means-style loop, capped for speed) and maps every pixel to its nearest palette entry. uniform mode generates an evenly-spaced RGB grid instead - crude, but useful for a locked "system palette" feel. custom_json locks to a palette you provide (hex colors or RGB triplets), which is the one to reach for when you need brand colors or a handheld console's actual palette to stay exact. The bayer_4x4 dither option adds ordered dithering to break up banding in gradients, with dither_strength (default 0.35) controlling how much.
The inputs that matter
- image - your IMAGE tensor (batches work, each frame gets processed and its own palette).
- palette_mode -
adaptive(default),uniform, orcustom_json. Adaptive is the best first try for photos and renders; custom_json is the professional choice for locked palettes. - color_count - 2 to 256, default 16. The size of the palette. Low counts (8–16) read as retro pixel art; higher counts start to look like a posterized photo.
- dither_mode -
noneorbayer_4x4. Turn dither on when you see ugly banding in skies or gradients. - preserve_alpha - default on. Keeps the image's alpha channel through quantization. Off fills with opaque.
- palette_json - used when mode is
custom_json. If you requestcustom_jsonwithout a valid palette, it warns and falls back to adaptive - a graceful failure you should still read the summary for.
The outputs
- image - the quantized result, same size as the input. Note it doesn't downscale; pixel-art size reduction is a resize job, this is purely color reduction. Pair it with a pixel-scale-down for the full sprite look.
- palette_json - the palette colors for the first frame. This is the sleeper feature: wire it into a manifest or use it as the locked palette for the next image so a whole batch shares one consistent palette.
- summary_json - mode, warnings, per-frame palette details.
Installing
Pack install:
cd ComfyUI/custom_nodes
git clone https://github.com/criskb/MKRShift_Nodes
restart, or Manager → "MKRShift". No dependencies, no models - it's numpy and PIL.
Gotchas
The honest ones: this is color quantization, not an art style. It can't fix a broken render into good pixel art - it makes a reduced-palette version of whatever you give it, so feed it clean, well-lit material. Dithering at 0.35 on a 16-color palette is a reasonable starting point, but you'll want to tune it per image; dither makes pixel art look "scanlined" if you overshoot. And don't expect 256 colors to look continuous - 256 is still a big reduction from truecolor. The README's suggested pairing is MKRSpriteSheetExtract + MKRImageCombineGrid for a full atlas cleanup workflow, and that's a genuinely nice combination if you're building sprite sheets.
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| palette_mode | COMBO | adaptive | 3 options: adaptive, uniform, custom_json |
| color_count | INT | 162–256 | — |
| dither_mode | COMBO | none | 2 options: none, bayer_4x4 |
| dither_strength | FLOAT | 0.350–1 | — |
| preserve_alpha | BOOLEAN | true | — |
| palette_json | STRING | [] | — |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| palette_json | STRING | — |
| summary_json | STRING | — |