SpriteFusion Pixel Snapper
AI Didn't Make Pixel Art — This Node Snaps It Into Shape
- image
- image
- grid_width
- grid_height
- mask
- key_color_used
Here's the thing nobody tells you until you've tried it: diffusion models don't make pixel art, they make things that look like pixel art from across the room. Zoom in and the "pixels" are different sizes, edges land between cells, and your 16-color sprite quietly has 300+ colors. SpriteFusion Pixel Snapper fixes exactly that - one node that takes your AI-generated pixel-art-adjacent image and turns it into a real, palette-limited image on a real grid.
It's the deterministic post-process the pixel-art community has been preaching for years, bundled into one node: detect the pseudo-pixel grid, quantize to a controlled palette, snap every cell to one color, then nearest-neighbor it back up for display. No model to download, no API key, nothing generative. Underneath it runs the actual Rust engine from Hugo-Dz/spritefusion-pixel-snapper - its author posted a now-452-score breakdown on r/StableDiffusion, and a commenter (SirMick, the mediapixelkr behind this pack) wrapped it for ComfyUI.
How it works
The node shells out to a Rust binary, one call per image. Rust does the heavy lifting: deterministic k-means quantization to your colors budget, then edge profiles to find the logical pixel size, then "walks" grid cuts toward meaningful edges instead of leaving them where the model dropped them. Python (plain PIL/numpy) handles the ComfyUI-specific parts: chroma-key transparency, output geometry, nearest-neighbor scaling, the mask.
Two grid modes matter. adaptive (default) lets every boundary move locally to follow an edge - right for irregular pseudo-pixels. uniform locks all cells to one global step per axis, for output that's already pixelated but slightly misaligned (Krea 2-style).
The inputs that actually matter
Thirteen inputs, but a beginner lives in about six:
colors(default 16) - max palette size for automatic quantization. Your "how retro" dial.pixel_size-0auto-detects the logical pixel size; set a value when detection guesses wrong or you want a specific sprite density.custom_palette- a fixed RGB palette, multiline-friendly (#RRGGBBor#RGB, up to 256 colors). Leave empty for automatic quantization. This is your get-out-of-jail card: k-means is area-weighted, so a big green background can eat the budget and fold a small red hat into green mush - a real complaint in the upstream thread. Hand it a named palette and small salient colors survive.output_mode-crop_to_input_aspect(default) crops the detected grid to your input's aspect,detectedkeeps it as-is,pad_to_input_aspectpads,exact_sizeresizes toexact_width/exact_height. A square input with a64x65grid becomes64x64.output_scale- integer nearest-neighbor enlargement so your64x64sprite isn't a dot on screen. This is the one place "pixel art upscaler" means integer replication, not a neural model.transparencywithkey_color-autopicks the background color shared by the four corners;connected_chroma_keyflood-fills matching regions from the edges, protecting internal details that share the background color;chroma_keynukes every matching pixel, even inside the character.
You get five outputs: the corrected image, the low-res grid_width/grid_height (before scaling), a mask (white = transparent, per ComfyUI convention), and key_color_used showing what auto-detection picked.
Installing it
Easiest path is ComfyUI Manager - search "SpriteFusion Pixel Snapper (Rust)" (or comfy node install spritefusion-pixel-snapper-rust), or git-clone manually:
cd ComfyUI/custom_nodes
git clone https://github.com/mediapixelkr/ComfyUI-SpriteFusion-PixelSnapper.git
Then restart ComfyUI and find it under image/pixel art. No model files and no heavy Python deps - install.py grabs a prebuilt Rust binary from GitHub Releases for Windows, Linux x86-64, and macOS Intel/Apple Silicon. No prebuilt for your platform? It falls back to cargo build --release, which needs Rust (and MSVC Build Tools on Windows). If the binary lives somewhere unusual, point SPRITEFUSION_PIXEL_SNAPPER_BIN at it before starting ComfyUI.
Where people get burned
- "Binary not found" on startup. The prebuilt download or build failed. Run
cargo build --releaseinside the pack folder, or setSPRITEFUSION_PIXEL_SNAPPER_BIN. - Batch with auto-detection errors out. If detection lands on different grid sizes per frame, the node refuses to stack them - set
pixel_sizeexplicitly or split the batch. A deliberate guard, not a bug. - Custom palette + chroma-key mismatch. The background gets mapped to the custom palette too, so a forced
key_colormust survive into that palette. Usekey_color=autowith custom palettes. - Inverted transparency. White mask = transparent, black = opaque, and
Join Image with Alphawants the mask on.alpha. Reverse it and your sprite is invisible on black.
It's not perfect - it won't invent detail the model never made, and mangled pixel art can still confound grid detection (use uniform mode or a manual pixel_size). But for placeholder sprites, game prototypes, or cleaning up Krea 2 output, it's the difference between "looks like pixel art" and "is pixel art." Two bundled workflow PNGs (drag onto the canvas) demo it with a fantasy and a Game Boy green palette.
Inputs (13)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| colors | INT | 161–256 | — |
| pixel_size | FLOAT | 0.00–1024 | — |
| output_mode | COMBO | crop_to_input_aspect | 4 options: crop_to_input_aspect, detected, pad_to_input_aspect, exact_size |
| output_scale | INT | 11–64 | — |
| exact_width | INT | 2561–16384 | — |
| exact_height | INT | 2561–16384 | — |
| transparency | COMBO | 3 options: connected_chroma_key, chroma_key, none | |
| key_color | STRING | auto | — |
| key_tolerance | INT | 320–255 | — |
| cell_method | COMBO | 3 options: majority, center_weighted, center | |
| custom_palette | STRING | — | |
| grid_mode | COMBO | adaptive | 2 options: adaptive, uniform |
Outputs (5)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| grid_width | INT | — |
| grid_height | INT | — |
| mask | MASK | — |
| key_color_used | STRING | — |