Transparency

A NextDAAD picture can have holes in it - pixels that show the text layer underneath instead of painting over it. Getting a hole right takes one rule. Getting it wrong produces a file that loads, displays, and is simply missing the holes you drew, with nothing on screen to tell you why.

Read this page before you paint your first hole.

The rule

A transparent pixel must carry palette index 255, and nothing else may.

NextDAAD's picture format states it directly: the interpreter stamps palette entry 255 with the transparent colour on every picture load, and any pixel drawn with index 255 becomes a hole showing the text layer beneath. Opaque artwork is quantised into indices 0 to 254 - 255 colours, not 256. Index 255 is emitted deliberately, for transparent pixels, or not at all.

In NextDither, and under the convention NextDAAD's kit uses throughout, the way you ask for a hole is to paint it in pure #FF00FF - RGB 255, 0, 255. The stock preset keys slot 255 to exactly that colour, so those pixels are routed straight to index 255 and come out as holes.

You do not put the colour in slot 255 yourself. The preset has already done it, and in the normal flow there is no control for changing it - the one exception is the fix button described further down, which appears only when a loaded preset's transparency slot is not keyed. Your side of the rule is entirely about what you paint into the source image.

One clarification that saves confusion later: the magenta itself is a convention, not the mechanism. The colour sitting in slot 255 is overwritten by the interpreter on load, so its value has no effect on the result - #FF00FF is used because it matches the sprite path and previews as obvious magenta in an editor. What the hardware and the interpreter care about is the index. Paint magenta, but understand that you are asking for index 255.

There is a second rule, and it is the reason the first one is so unforgiving:

No palette entry may carry a first byte of $E3, except index 255.

$E3 is the Spectrum Next's global transparency colour - full red, no green, both top bits of blue set. It is what the hardware transparency register holds after a reset, and the same colour Next sprites use.

Why slot 255 specifically

On the hardware, transparency is a colour compare, not an index compare. The Next matches the first byte of a palette entry and nothing else, so both nine-bit colours whose first byte is $E3 - the display colours (255, 0, 219) and (255, 0, 255) - would be transparent whatever index they sat at. The ninth blue bit cannot rescue an entry.

NextDAAD does not leave it there. Its loader defends against a stray reserved colour: an art entry whose first byte is $E3 is shifted two steps down the blue scale as the picture loads. It is still magenta, and in a picture the shift is imperceptible. Index 255 is the one entry exempt from that - the interpreter stamps it and leaves it alone.

Put those two together and you have the failure this page exists to prevent:

If your transparent pixels land at any index other than 255, they do not punch holes. The loader shifts that palette entry instead, so they render as a slightly different magenta - opaque, solid, and roughly the colour you painted. Under the stock preset this cannot happen. What causes it is a preset that nominates some other slot for transparency.

Nothing reports this. The file is well-formed, so the loader does not refuse it; a picture is only rejected when its size or shape is wrong. The picture appears. It just has magenta where it should have had holes, in a shade close enough to the one you chose that it reads as a paint mistake rather than a format mistake. NextDAAD's own format reference is blunt about the loader's defence: do not rely on it, because it alters your colour silently.

That is also why the exemption belongs to index 255 itself and not to whatever slot a preset happens to nominate for transparency. A preset that moves transparency to slot 200 does not earn slot 200 an exemption. The loader will shift it, the pixels come out opaque, and the holes never appear.

Hole edges must be hard

Paint holes with a hard-edged tool. No anti-aliasing, no feathering, no dithering across the boundary.

A soft fringe is not the key colour, so it is not routed to index 255. It is quantised as ordinary near-magenta and comes out opaque - a magenta halo tracing the outline of every hole you drew. That is NextDAAD's ruling, not a NextDither limitation.

NextDither matches the key colour at zero tolerance, deliberately. Anything wider would silently enlarge every hole past what you drew, and would blur the distinction the whole pipeline depends on: an exact match is a hole you asked for, and anything else near the reserved value is an accident to be nudged aside. Paint a block of #FA00FA - visually almost identical - and those pixels stay opaque and are not counted as transparent. Only the exact colour makes a hole.

Two consequences worth having straight:

  • The hard edge is required in your source file, not in the output. Which pixels are holes is decided against the colours you painted, before framing and before any Adjust setting is applied, and the resulting mask is resampled with nearest-neighbour whatever resize filter you have chosen. So downscaling a picture, or nudging Brightness, cannot erode or erase a hole. Draw the hard edge once, in the artwork.
  • Nothing you do in the Adjust group can invent a hole either. Because designation happens against your source colours, an ordinary colour that a saturation or brightness change happens to push onto #FF00FF is not treated as a hole.

What NextDither does about it

You do not have to configure any of this. The stock preset already keys slot 255 to #FF00FF at zero tolerance, marks slots 0 to 254 free, and forbids the $E3 first byte everywhere else. Several things then work to keep you out of trouble:

Slot 255 is never the quantiser's to use. A keyed slot is not allocatable, so the nearest-colour search can never hand index 255 to ordinary artwork. And no generated palette entry is allowed to land on the reserved value at any index: a colour that would is moved to the nearest permitted colour before it reaches the file.

The two preview panes deliberately disagree about a hole. The source pane shows the magenta you painted. The result pane paints those same pixels as a grey checkerboard, because that is what the file contains - not a colour, a hole. This is correct, not a bug, and it is genuinely useful: press V for single view and hold Space to flip, and the checkerboard shows you exactly which pixels became transparent. See The preview.

The status bar counts holes. A transparent-pixel figure appears only when the picture has holes, so an ordinary opaque conversion shows nothing at all. If the count reads 81920 transparent (the whole picture) you have exported nothing but a hole.

The dock rings a colliding entry. Slot 255 carries a white T, and any entry whose first byte packs to $E3 at an index other than 255 gets an amber collision ring. The exemption from that ring follows literal index 255 and nothing else: a preset nominating slot 200 for transparency earns slot 200 no exemption, so slot 200 rings too. That is deliberate, and it is precisely the case worth flagging. See The palette for the dock.

A separate warning covers a colliding pinned or keyed slot - but only at a slot the preset has not nominated for transparency. Where it fires it is explicit: pixels routed there do not come out as a hole, the loader shifts the entry two steps down the blue scale, they render as a slightly different magenta than you painted, and if you wanted transparency you should move the colour to slot 255.

The ring and that warning are not a pair, and it matters which is which. The ring is the wider net; the warning falls silent at whichever slot the preset has nominated, on the reasoning that pinning or keying the transparency slot to the reserved colour is what the stock preset itself does and must not warn every conversion. The consequence is spelled out under the batch check below.

An unkeyed transparency slot is called out. If the loaded preset has slot 255 pinned rather than keyed, it still parses and still converts - it just quantises #FF00FF as an ordinary colour instead of making holes. When that is the case, an amber line appears in the dock:

The transparency slot is not keyed. Pixels painted #FF00FF will be quantised as ordinary colour, not turned into holes.

beside a button reading Set it to keyed #FF00FF. Pressing it fixes the setting in memory and marks the preset as modified; nothing is written to disk until you choose Preset > Save. The warning stays visible even when the dock is collapsed, because collapsing the dock for space must not dismiss a warning about artwork losing its holes on export.

A batch run refuses outright. If the loaded preset puts transparency on any slot other than 255, batch conversion stops before converting anything and says so:

This preset puts transparency on palette slot 200. Only slot 255 works: the interpreter stamps that entry on load and leaves it alone, and shifts every other one, so pixels you painted #FF00FF would come out opaque and the holes would never appear. No files were written.

Note the limit of that check: it guards a batch run only. Saving a single picture does not run it, and it is worth being exact about what is left, because it is less than you might reasonably expect.

Take the case that check exists for - a preset nominating slot 200, with slot 200 keyed to #FF00FF. On the single-image path:

  • The amber collision ring appears on slot 200. This fires.
  • The pinned-or-keyed collision warning does not fire. It is suppressed at whichever slot the preset nominates, and this preset nominates 200, so the warnings list stays empty.
  • The unkeyed-slot warning does not appear either. It fires only when the transparency slot is not keyed, and slot 200 is keyed here - the preset is correctly shaped at the wrong index.
  • File > Save As writes the file.

So on the single-image path the amber ring is the only thing that tells you. If you have hand-edited a preset's transparency slot, look at the dock before you save - or run the picture through the batch path, which refuses outright and names the slot.

One further consequence of moving transparency off 255: unless the preset also blocks index 255, that index goes back to being the quantiser's to allocate, and NextDAAD's format is blunt about what follows - a plain quantisation that puts stray pixels on index 255 makes "every one of them a hole". So the same edit can lose the holes you drew and add holes you did not.

Confirming a hole is a hole, before you export

Three checks, none of which take more than a moment:

  1. The result pane checkerboards it. If the region you painted still looks like solid magenta in the result pane, it was not recognised as a hole - almost always because the colour is not exactly #FF00FF. Zoom in and check the edges too: a checkerboarded centre with a magenta outline is an anti-aliased edge.
  2. The status bar shows a transparent count. No figure at all means the picture has no holes in it.
  3. The dock's slot 255 is filled, not hollow, and carries its T. Click it: the detail line reads Keyed, and its pixel count is the number of transparent pixels in the picture.

If all three agree, the file you export has holes in it.

Verifying an exported file. NextDAAD's authoring kit includes authoring-kit/lib/palcheck.ps1, which audits a converted file's transparency: it warns about a palette entry colliding with the reserved colour and names the index, and it reports how many pixels use index 255 as a count rather than a warning, since deliberate transparency is legitimate. It is advisory - it warns and exits 0 - and it only understands uncompressed files.

Letterbox padding is a hole too

Under the Letterbox fit, the bars NextDither adds around an image that does not match the frame's aspect ratio are routed to the transparency slot as well. They are holes, not black bars: on hardware the text layer shows through them.

That is usually exactly what you want, and it is the stock preset's behaviour - 320x256, Letterbox, anchored top centre - so a source picture that is not already 320x256's shape gets transparent padding without your asking for it. The result pane checkerboards those bars for the same reason it checkerboards a painted hole: nothing is there.

It is occasionally a surprise, so it is worth stating plainly. If you want opaque bars rather than holes, do not letterbox - use Crop to fill, match the frame's aspect ratio in your artwork, or paint the bars into the source image yourself so they become ordinary colour. See Adjusting an image for the Fit and Anchor controls.

One related warning you may see: if a picture has letterbox padding but the preset sets no transparency slot at all, the padded pixels have no index to resolve to, and the conversion says so rather than guessing.