Troubleshooting

Symptoms to causes. Each entry says what you see, why it happens, and what to do about it.

Holes do not appear in the game

What you see: the picture loads and displays fine, but the areas you painted as holes show as solid (or slightly shifted) magenta instead of the text layer showing through.

Why: on real hardware, transparency is a colour compare, not an index compare - any palette entry whose first byte is the reserved value $E3 is transparent, whatever index it sits at. NextDAAD's loader defends against that: it stamps index 255 with the transparent colour and leaves it alone, but shifts every other $E3 entry two steps down the blue scale as the picture loads, so it survives as a slightly different, opaque magenta instead of punching a hole. If the loaded preset's transparency_slot is anything other than 255, NextDither still produces a well-formed file; it is just one where the holes you painted quantise as ordinary opaque colour instead. Whether anything warns you about this depends entirely on which path you exported through:

  • Batch conversion refuses outright and names the slot before writing anything - see Batch conversion.
  • File > Save As does not check this at all. The refusal above is a batch pre-flight check (crates/nextdither-gui/src/preflight.rs, the slot check at the top of preflight()); there is no equivalent gate on the single-image path.
  • The warning that would normally flag a pinned or keyed slot colliding with the reserved value is also silent here, and deliberately so: it never fires at whichever slot the preset itself nominates for transparency, since keying that slot to the reserved colour is exactly the correct, intended configuration when the nominated slot is 255 (crates/nextdither-core/src/palette/mod.rs, error.rs). When the nominated slot is something else, the warning still stays silent there - the same rule, just guarding the wrong slot.

The one thing that does still show it: the amber collision ring in the palette dock, on whichever slot the preset actually nominates. See Transparency for the full account of what each path does and does not catch.

What to do: before saving a single picture from a hand-edited preset, check the palette dock for an amber ring, or check the preset file's transparency_slot and its  entry directly (see The preset file). If in doubt, run the same image through Batch instead of Save As - it will refuse and name the slot if something is wrong.

A magenta halo rings a hole

What you see: the centre of a painted hole punches through correctly, but a thin fringe of magenta survives around its edge.

Why: a hole is matched by exact colour at zero tolerance. An anti-aliased, feathered, or dithered edge is not exactly the key colour, so those fringe pixels are quantised as ordinary near-magenta and rendered opaque - that is NextDAAD's ruling on hard edges, not a NextDither limitation. See Transparency: hole edges must be hard.

What to do: repaint the hole with a hard-edged tool - no anti-aliasing, no feathering, no dithering across the boundary. Press V for single view and hold Space to flip between panes: a checkerboarded centre with a magenta outline is the tell-tale sign of a soft edge.

Batch refuses to start

What you see: pressing Run shows an amber line and a Dismiss button; nothing converts.

Why: one of the batch pre-flight's four checks failed before any row ran - a transparency slot other than 255, two outputs resolving to the same path, an unwritable output folder, or a naming pattern that cannot produce a valid filename. See Batch conversion: the pre-flight checks.

What to do: read the amber line - it names exactly which check failed, and for the transparency and duplicate-target cases it names the specific slot or path involved.

Batch output is named by number when you wanted source names

What you see: the output folder fills with 000.NX2, 001.NX2 and so on, rather than filenames matching your source images.

Why: the Out tab's naming pattern resolves to something like {n:3}.{ext} rather than {stem}.{ext} - perhaps carried over from a preset saved before the stock preset's default naming changed, or set deliberately for a queue you meant to renumber.

What to do: check the resolved name column in the batch queue before running - it always shows the name a run right now would actually produce. Set the naming pattern to {stem}.{ext} (the current stock default) or any custom pattern that includes {stem} if you want outputs named after their sources. See Batch conversion: the resolved name column and The preset file.

The game build fails on a picture NextDither wrote

What you see: NextDAAD's authoring kit build script stops hard while staging a picture NextDither produced, rather than skipping it.

Why: the kit's staging routine (authoring-kit\lib\gfx.bat, :stage_picture) reads everything before the first dot in a staged filename as the picture's number, and requires that to be digits only, at most three of them - anything else stops the whole build via :stage_picture_bad rather than being skipped quietly. The stock preset's naming pattern is {stem}.{ext}, which keeps the source filename verbatim: kitchen.png becomes kitchen.NX2, and "kitchen" is not a number, so the build fails the moment it reaches that file.

Switching the pattern to {n:3}.{ext} does not always fix this either. OutputNaming::resolve (crates/nextdither-core/src/preset.rs) takes all of a stem's leading digits with no upper limit, and the width specifier in {n:3} only pads a short number - it never truncates a long one. A source named 2024_hallway.png therefore resolves to 2024.NX2, four digits, which the kit's own check rejects exactly as it rejects kitchen.NX2.

What to do: check the resolved name column before running the kit's build, for any source file whose name does not already look like a bare 1-3 digit picture number. Neither of NextDither's two built-in naming patterns is safe for every possible source filename by itself.

A batch is slow

What you see: a run takes noticeably longer than converting one image by hand would suggest.

Why: ZX0 compression costs roughly 8.6 seconds per 320x256 image in a release build, against roughly 17ms for the rest of a full conversion. If Bitmap, ZX0 compressed is ticked in the Out tab, a batch of N images pays that cost N times over. The stock preset does not tick it by default for exactly this reason - see Exporting.

What to do: untick ZX0 for a fast pass over a large batch, and compress only the images you actually need it for; or expect roughly N x 8.6 seconds for an N-image ZX0 batch and let it run - the window stays fully responsive throughout.

The app appears to do nothing after Cancel

What you see: pressing Cancel during a run does not seem to change anything for several seconds.

Why: Cancel does not abort a row already converting - core lets rows already in flight finish rather than aborting mid-write. The button's own label says so, changing to Cancelling - finishing N images already started, and with ZX0 ticked that can be several real seconds per row already under way. See Batch conversion: Cancel.

What to do: watch the N in the Cancel button's own label count down to zero. The window stays fully responsive throughout - dragging or resizing it is a good way to confirm nothing has actually frozen.

Colours look wrong after freezing the palette

What you see: ticked Freeze palette, then moved Brightness, Contrast, Gamma or Saturation, and the picture now looks banded or otherwise worse than before.

Why: this is the freeze doing its job, not a fault. A frozen palette stops rebuilding itself as you adjust tone, so a later tone change is mapped onto the palette chosen for the image's original tones rather than a freshly rebuilt one - banding after a large Brightness push is the expected result. See The palette: Freeze palette.

What to do: untick Freeze palette to let the palette rebuild for the current tones (this also discards the captured palette). If you only meant to change the quantiser mode, note that loading a preset which changes only palette_mode does not itself drop an existing freeze - you will need to untick it by hand.