Preset File
A preset is the whole settings tree - frame, adjust, dither, palette mode and colour count, the slot policy, the transparency slot, and export - saved as one TOML file (crates/nextdither-core/src/preset.rs). It is meant to be hand-edited and kept beside a game's project, not only inside NextDither's own managed presets folder. See Presets for Save, Save As and where a preset can live.
This page walks the stock NextDAAD Layer 2 preset field by field, as checked into the source tree at crates/nextdither-core/presets/nextdaad-layer2.toml. Its content is exactly what Preset > Save As... would write if you saved the built-in preset's current settings under a new name without changing anything - the built-in preset itself is compiled in and cannot be overwritten, but its values can always be saved out this way (see Presets).
name = "NextDAAD Layer 2"
forbidden_hi_bytes = ["0xE3"]
transparency_slot = 255
palette_mode = "adaptive-wu"
colour_count = 255
[frame]
width = 320
height = 256
fit = "letterbox"
anchor = "top-centre"
filter = "lanczos3"
[adjust]
brightness = 0.0
contrast = 0.0
gamma = 1.0
saturation = 0.0
filter = "none"
perceptual = false
[dither]
strength = 1.0
serpentine = true
metric = "oklab"
[dither.algorithm]
diffusion = "floyd-steinberg"
[export]
formats = ["bitmap"]
zx0_mode = "single-stream"
overwrite = "prompt"
short_zx0_extensions = false
[export.naming]
pattern = "{stem}.{ext}"
from = 0
to = 254
kind = "free"
from = 255
to = 255
kind = "keyed"
colour = "#FF00FF"
source = "#FF00FF"
tolerance = 0
Top-level fields
name- the preset's display name, shown in the toolbar and the Preset menu.forbidden_hi_bytes- palette high bytes (the packedRRRGGGBBbyte) that may never appear in a generated palette entry, as hex strings such as"0xE3". The stock preset forbids0xE3, NextDAAD's reserved transparency byte, everywhere except the transparency slot itself. See File formats for what that byte means.transparency_slot- which palette index NextDither treats as the hardware-transparent one, or omitted entirely for artwork with no holes. Only 255 actually works on hardware - the interpreter stamps that entry on every load and leaves every other one to be shifted off the reserved colour if it collides. A preset naming any other slot here still parses and still converts; the pitfall that creates is covered next.palette_mode- which quantiser builds the palette:"adaptive-wu","adaptive-k-means", or"rgb332"(the fixed identity palette). Required - a preset with this field missing or misspelled fails to load rather than silently falling back to a default, since a preset is meant to be a complete, hand-editable settings tree. See The palette for what each mode does.colour_count- how many colours the quantiser may allocate. It is clamped to however many slots thepolicy leavesfree, with a warning, if it asks for more than that. The stock preset asks for 255 because its policy leaves exactly 255 slots free (0-254; slot 255 is keyed, not free).
[frame], [adjust], [dither]
These mirror the Image tab's three groups directly - see Adjusting an image for what each control does. Their TOML shapes:
[frame]:width,height(pixels);fitis one of"stretch","letterbox","crop-fill";anchoris one of the nine-way grid values ("top-left","top-centre","top-right","centre-left","centre","centre-right","bottom-left","bottom-centre","bottom-right");filteris one of"nearest","triangle","catmull-rom","lanczos3".[adjust]:brightness,contrast,saturation(each -1.0 to 1.0, 0.0 neutral);gamma(greater than 0.0, 1.0 neutral);perceptual(bool);filteris either the bare string"none", or a table naming a spatial filter with its own parameters -[adjust.filter.sharpen]withradius,amountandthreshold, or[adjust.filter.blur]withradiusalone. These four ranges are enforced: a hand-edited value outside them fails to load, or fails before conversion, with a message naming the field.[dither]:strength(0.0 to 1.0),serpentine(bool),metric("weighted-rgb"or"oklab");algorithmis either the bare string"none", or a table naming a kernel -[dither.algorithm]with adiffusionkey (one of"floyd-steinberg","jarvis-judice-ninke","stucki","burkes","sierra3","two-row-sierra","sierra-lite","atkinson","stevenson-arce") or anorderedkey (one of"bayer2","bayer4","bayer8","bayer16","blue-noise").strength's range is documented, not enforced - unlike the[adjust]fields above, nothing checks it at load or before conversion, so a hand-editedstrength = 5.0is accepted silently rather than rejected or clamped.
[export] and [export.naming]
formats- a list of any combination of"bitmap","bitmap-zx0","nxp","png". The stock preset writes["bitmap"]only - ZX0 is a deliberate tick, not a default, because it costs real time per image (see Exporting).zx0_mode-"single-stream"(the default, and slightly smaller since pixel data may back-reference palette bytes) or"two-stream"(palette and pixels compressed as two separate streams, matchinggfx2next -zx0 -pal-embed).overwrite-"prompt","skip"or"overwrite", governing what happens when aSave Astarget already exists. See Exporting for what each does there. This setting has no effect on a batch run. A batch always asks when some of its outputs already exist, whatever this field is set to, and otherwise writes without asking; whichever button you press at that question is stamped over this field before the run submits. See Batch conversion for that question.short_zx0_extensions- when true, a ZX0 export is named with the 8.3 synonym (.N2Z/.NXZ) instead of appending.ZX0. See File formats.[export.naming].pattern- the batch naming pattern (Save As never uses this; it always uses the name typed into its own dialog). Supports{n:1}through{n:6}for a zero-padded queue number,{stem}for the source filename without its extension, and{ext}forNX2orNXIby frame width. The number comes from a row's own leading digits where its stem has any, and otherwise from its position in the queue; the width specifier only pads a short number, it never truncates a long one - see Troubleshooting for the batch-naming pitfall that causes, and Batch conversion for the full naming discussion.
: a run-length-encoded policy, not one line per slot
Read this before hand-editing the slot policy. Each entry is not one palette slot - it is a run of consecutive slots, from and to inclusive, that all share one state. The stock preset's two entries above cover all 256 slots: 0 to 254 (255 slots) as one free run, and 255 to 255 (one slot) as a keyed run. A 256-line preset with one entry per slot would parse identically, but nobody could read or diff it, which is the whole reason the format is run-length encoded in the first place.
from must be less than or equal to to in every run - the reverse fails to load rather than silently expanding to nothing. Runs may overlap, and where they do, later runs in the list win over earlier ones for the slots they share. That is deliberate, not a hazard to route around: it is the idiom for a broad free run followed by a narrower pinned, blocked or keyed override further down the list.
The four kind values
free- a quantiser may allocate it and the ditherer may emit it. Needs no extra fields.blocked- never allocated, never emitted, written to the file as black. Needs no extra fields.pinned- a fixed colour, written to the file but never chosen by a quantiser or the ditherer. Takes one extra field,colour, a#RRGGBBhex string (the leading#is optional).keyed- source pixels withintoleranceof a given colour are routed straight to this slot, bypassing quantisation and dithering entirely. Takes three extra fields:colour- the fixed colour written into this palette slot.source- the source-image colour to match against, as a#RRGGBBhex string. In the underlying Rust type this field is namedfrom, but it is serialised assourcehere on purpose: a run's ownfrom(its first slot index) already occupies that key, and writingfrominside a keyed block instead ofsourcecollides with it - the colour string silently overwrites the index and the preset fails to load with an error naming au8where a string was found. Usesource, notfrom, inside akeyedblock.tolerance- the maximum per-channel difference (0-255) between a source pixel andsourcethat still counts as a match. The stock preset uses 0, and that is a requirement for a hard hole edge, not an arbitrary choice: any tolerance wide enough to catch an anti-aliased fringe would enlarge every hole past what was actually painted, and blur the distinction between a deliberate hole and an ordinary near-magenta colour. See Transparency.
The pitfall: moving transparency off slot 255 can also add holes nobody drew
Editing transparency_slot and the keyed run together to move transparency onto some other slot does not just risk losing the holes you intended - see Troubleshooting for that half. It can also add holes nobody drew: unless the edited policy also blocks or pins slot 255, removing its keyed run leaves 255 covered by nothing more specific than the broad free run underneath it, so it goes back to being an ordinary slot the quantiser is free to allocate. NextDAAD's picture format is explicit about what a plain quantisation onto index 255 then does: every pixel that lands there becomes a hole, whether you meant one or not. The full account of this is in Transparency - one edit to the list can touch both halves of the transparency rule at once, and it is worth checking the palette dock's slot 255 after making it.