Nodes/MKRShift_Nodes/Pixel Palette Reduce
ComfyUI Node

Pixel Palette Reduce

Palette reduction that outputs the palette

By criskb·Created 7 months ago·Updated 5 months ago· 0
Pixel Palette Reduce
  • image
  • image
  • palette_json
  • summary_json
palette_modeadaptive
color_count16
dither_modenone
dither_strength0.35
preserve_alphatrue
palette_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, or custom_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 - none or bayer_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 request custom_json without 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.

CategoryMKRShift Nodes/Utility/Pixel

Inputs (7)

NameTypeDefaultDescription
imageIMAGE
palette_modeCOMBOadaptive3 options: adaptive, uniform, custom_json
color_countINT162–256
dither_modeCOMBOnone2 options: none, bayer_4x4
dither_strengthFLOAT0.350–1
preserve_alphaBOOLEANtrue
palette_jsonSTRING[]

Outputs (3)

NameTypeDescription
imageIMAGE
palette_jsonSTRING
summary_jsonSTRING