PixelOE
Pixel art that isn't just a shrunk-and-blurred photo
- img
- pixel_image
- oe_image
- oe_weight
Most "make this pixel art" tools do one thing: shrink the image, then blow it back up with nearest-neighbor scaling. You get big, blocky pixels, but not pixel art - real pixel art has clean, deliberate outlines and a tight palette, and a naive downscale just averages detail into mush. PixelOE is KohakuBlueleaf's answer to that: contrast-aware outline expansion before the downscale, so edges stay crisp instead of turning to soup. It's good enough that when Fal.ai shipped a paid "image-to-pixel-art" API that turned out to be plain downscaling, someone on r/StableDiffusion ran the same image through PixelOE and got a visibly better result for free.
How it works
Before shrinking anything, PixelOE looks for high-contrast edges and expands them outward - thickening outlines the way an artist would ink over a rough sketch - so that when the image is downsampled, those edges survive as clean pixel boundaries instead of getting blurred into neighboring colors. Only after that does the actual downscale happen, using whichever method you pick, optionally followed by color quantization and dithering to flatten the palette down to something a retro sprite sheet would actually use.
The inputs and outputs that matter
img- your source image.pixel_size(1–32, default 4) - how many source pixels collapse into one "pixel" block. Bigger number, chunkier art.thickness(0–6, default 2) - how aggressively outlines get expanded before downscaling. This is the setting that actually makes it look like pixel art instead of a shrunk photo.mode- the downscale algorithm:contrastis PixelOE's own outline-aware method (the one you want),k_centroidclusters colors per block, andlanczos/nearest/bilinearare the plain, non-aware downscales for comparison.color_quant(bool) +num_colors(2–256) +quant_mode(kmeans/weighted-kmeans/repeat-kmeans) - turn this on and dropnum_colorsto 16–64 if you want an actual limited retro palette instead of full 24-bit color squished into big squares.dither_mode-ordered,error_diffusion, ornone, applied when quantizing.no_post_upscale(bool) - leave off to get the pixelized image scaled back up to your original size with hard, blocky pixels (what most people want); turn on to keep the small, true-resolution version.device-default/cpu/cuda/mps. Leave on default unless you're troubleshooting.
Three outputs: pixel_image is the finished result - wire this into your Save node. oe_image is the outline-expanded image before downscaling, useful if you just want the beefed-up-outline look without the pixelization. oe_weight is the weight map the algorithm used to decide where to expand - mostly a debugging/inspection output, not something you'll normally wire anywhere.
How to install it
Search ComfyUI-HakuImg in ComfyUI Manager and install, or from the command line:
cd ComfyUI/custom_nodes
git clone --recurse-submodules https://github.com/licyk/ComfyUI-HakuImg
Restart ComfyUI afterward. The node shows up under Image → HakuImg. No models to download - this is pure image processing, nothing hits the GPU unless you explicitly set device to cuda.
Common issues & troubleshooting
Only PixelOE/OutlineExpansion/PreResize are missing after install, everything else works. That --recurse-submodules flag in the clone command isn't decoration - PixelOE's actual downscale algorithm ships as its own git submodule, and a plain git clone (or an installer that doesn't handle submodules) silently skips it. Check ComfyUI's startup console for an import error on this pack, then re-clone with the flag.
Output looks like a blurry shrunk photo, not pixel art. You're probably on mode: lanczos/bilinear/nearest with thickness low. Switch to mode: contrast and push thickness up a notch or two - that's the entire point of this node over a plain resize.
Colors look muddy or too "photo-real" for the style. Turn on color_quant and bring num_colors down into the 16–64 range. Full palette plus big blocky pixels is the classic mistake - real 16-bit-era sprites had a handful of colors per object, not thousands.
Slow on large images. It's CPU-bound by default; set device to cuda if you have a GPU available and it's fighting for time.
Inputs (10)
| Name | Type | Default | Description |
|---|---|---|---|
| pixel_size | INT | 41–32 | — |
| thickness | INT | 20–6 | — |
| img | IMAGE | — | |
| mode | COMBO | 5 options: contrast, k_centroid, lanczos, nearest, bilinear | |
| color_quant | BOOLEAN | false | — |
| no_post_upscale | BOOLEAN | false | — |
| num_colors | INT | 2562–256 | — |
| quant_mode | COMBO | 3 options: kmeans, weighted-kmeans, repeat-kmeans | |
| dither_mode | COMBO | 3 options: ordered, error_diffusion, none | |
| device | COMBO | 4 options: default, cpu, cuda, mps |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| pixel_image | IMAGE | — |
| oe_image | IMAGE | — |
| oe_weight | IMAGE | — |