## The pipeline
Every image runs through the same stages. Each one is skippable and configurable, and everything the pipeline learns is reported back to you.
-
Grid detection
The image is reduced to gradient profiles and scanned with a fractional comb — pitch doesn't have to be an integer, so a 48-cell sprite rendered at 512px detects as exactly 10.667px per cell. When confidence is too low to trust, the pipeline says so and leaves your image unsnapped instead of inventing a grid.
-
Anti-aliasing removal opt-in
Pixels that sit on the interpolation line between their two dominant neighbors snap to the closer one, over multiple passes for wide AA ramps. A guard protects checkerboard dithering from being eaten.
-
Background & chroma masking
Border colors are clustered in OKLAB (gradients and vignettes work), masks are built at full resolution where fringes are one pixel wide, and chroma keys remove their color everywhere. Nothing is cleared yet — the mask rides along so the next stage can't bake halos into blocks.
-
Grid snapping
Each block votes on its color with near-duplicates pooled in OKLAB, and the winner paints its most representative actual color — never an invented average. Alpha binarizes per block for clean game-asset edges. Blocks with contested votes are flagged so you can spot a bad grid fast.
-
Dither detection
Checkerboard and row/column alternation between color pairs is detected on the logical grid. Dithering is how pixel art fakes gradients — it looks better kept, so detected pairs get pinned through quantization.
-
Quantization
Seeded weighted k-means over unique colors — deterministic, exact, and rare structural colors like 1px outlines can't be dropped. Palettes come from extraction, built-ins, .hex files, or Lospec; matching uses hyAB so dark blues become navy, not black.
-
Output
Snap mode keeps the original resolution. Reduced modes emit the true logical size or a crisp integer re-upscale — never a stretch back to dimensions that recreate the misalignment you just removed.
## Quick start
Clean up an AI sprite (grid auto-detected):
$ pixfix process sprite.png
Grid: 10.667x10.667 pixels, phase: (0, 0), confidence: 85%, logical: 48x48
Saved: sprite_normalized.png
Inspect before you commit to anything:
$ pixfix analyze sprite.png --json | jq .grid
Reduce to true resolution with a 16-color palette and transparent background:
$ pixfix process sprite.png out.png \
--downscale-mode majority-vote --logical-size --colors 16 --remove-bg
Key out a green screen (works on interior regions too):
$ pixfix process sprite.png out.png --chroma-key 00FF00
A whole directory in parallel, PICO-8 palette:
$ pixfix batch ./sprites out/ --palette pico-8
## Try it
The same pipeline, compiled to WebAssembly. Everything runs right here in the page — your image is never uploaded anywhere.
The in-browser demo couldn't start here (it needs WebAssembly and a normal web context). The CLI does everything it does and more.
## Commands
process | Normalize a single image, or an animated GIF with one grid and one palette shared across frames. - pipes through stdin/stdout. |
analyze | Grid, background, and color inspection with zero writes. |
batch | Parallel directory/glob processing; collisions detected up front, partial failures itemized. |
sheet | Split, normalize, and reassemble sprite sheets — fixed grid or auto-split. |
palette | List built-ins, extract from an image, fetch from Lospec. |
tui | Interactive terminal editor (build with --features tui). |
## The flags that matter
--grid-size 10.667 | Force a pitch, fractional welcome. --grid-phase X,Y for the offset. |
--coarsen 2 | Multiply the final pitch (detected or forced) by an integer: detection finds the render quantum, but the intended art resolution is often 2x coarser. Phase preserved; no effect when detection declines. |
--downscale-mode | snap (default, keeps resolution) · majority-vote · center-weighted · center-pixel |
--logical-size | Reduced modes: emit true logical resolution instead of the crisp integer re-upscale. |
--colors N / --palette / --lospec / --palette-file | Mutually exclusive palette sources. |
--remove-bg | Border-clustered background removal with de-fringing. --bg-color, --bg-tolerance, --no-flood-fill tune it. |
--chroma-key HEX | Green-screen removal, everywhere in the image. Repeatable; --chroma-tolerance tunes the match. |
--keep-alpha | Preserve per-pixel alpha instead of binarizing blocks opaque/transparent. |
--flatten-dither | Turn off dither preservation entirely. |
--aa-threshold 0.5 | Enable AA removal; higher is more aggressive. --aa-passes for wide ramps. |
--seed N | Determinism: same input + seed → identical bytes. |
Full reference: pixfix help process or the
README.
Config files (.pixfix.toml) fill in anything the CLI doesn't set.
## For scripts & agents
The CLI is built to be driven blind. --json puts exactly one
machine-readable document on stdout — stable schema, fields always present —
and keeps logs on stderr. Exit codes are distinct, outputs are no-clobber by
default, and llms.txt / llms-full.txt
carry this whole manual in plain text.
$ pixfix process sprite.png out.png --json
{
"grid": { "pitch_x": 10.667, "phase": [0.0, 0.0], "confidence": 0.85, "source": "detected" },
"logical_size": [48, 48],
"colors_after": 16,
"low_confidence_blocks": 3,
"warnings": [],
"duration_ms": 412
}
| 0 | success | 4 | processing failed |
| 2 | usage or config error | 5 | output exists (pass --overwrite) |
| 3 | input unreadable | 6 | batch partially failed |
## pixfix, the desktop app
The same pipeline with live before/after previews: drop an image in, scrub the settings, watch it re-process. Grid scores, contested blocks, and color histograms are right there in the diagnostics pane. Bundles ship for macOS, Linux, and Windows on every release.
Grab it from the releases page.