QUICKUPSCALE

PRACTICAL GUIDE

Pixel-art PNGs and transparency

Swap a sprite palette without changing its outline

Start here

Replace exact RGBA tuples and keep each target alpha equal to its source alpha. Mapping opaque teal to purple and orange to pink changed exactly 82 pixels in the original 16×16 sprite; its 40 outline pixels, two eyes and every decoded alpha value remained unchanged.

Expected result:

The actual recolored PNG matched simultaneous replacement across all 256 decoded input pixels: 82 changed and 174 unchanged, with zero alpha changes. The change mask matched separately and was not part of the result ZIP.

What you need

Use the original 16×16 sprite. It includes opaque artwork, three partial-alpha controls and 129 transparent pixels. The CC0 project contains raw source/reference versions, not substitutes for the actual browser outputs below.

Tested with: Desktop Google Chrome 154.0.8037.44; browser UAData reports macOS platformVersion 27.0.1 and arm/64. Actual Cloudflare bundle cccc66d; independent window OffscreenCanvas reference and Python 3.14.8/Pillow 12.3.0 verification. No production-worker raw capture or physical-device acceptance.

1. Enter two complete RGBA replacements

Open the exact palette swapper, select sprite-a.png with Choose PNG, and add these rows. Each field requires eight hexadecimal digits in RRGGBBAA order:

30b090ff → 8848c0ff
f8b848ff → e86080ff

Press Replace exact colors. The first row changes 70 opaque teal pixels; the second changes 12 opaque orange pixels. Keeping ff as each target alpha is part of this outline-preserving recipe.

Actual purple and pink recolored sprite with the original decoded outline and eyes
16×16px. Actual recolored tool PNG, independently checked against the decoded input.

2. Verify the untouched pixels and alpha

The actual report records 82 matched/changed pixels, 174 unchanged pixels, zero alpha changes and zero hidden-only changes. Independent full-plane comparison confirmed all 40 outline positions, two eyes, three decoded partial-alpha controls and 129 decoded transparent positions were unchanged by the operation.

Download ZIP saves replacement.zip with manifest.json, replacement.json and sprite-a.png. The saved report and PNG matched their ZIP members byte for byte. The actual manifest keeps the original 16×16 dimensions and the decoded pixel basis.

3. Read the change mask separately

Actual magenta change mask marking the 82 recolored sprite pixels
16×16px. Actual displayed OR mask, separate from the recolored PNG and absent from the result ZIP.

Magenta marks a bin containing at least one changed decoded source byte. Here the source is small enough for one mask pixel per source pixel. Larger previews aggregate positions into at most 128×128 bins; they do not count changed pixels by visible mask area.

Mappings apply once to original pixels, without cascades. Empty, identity or absent-color maps can produce no changes. A separate actual 272-color input succeeded with an empty map and zero changed pixels: the extractor’s 256-key limit does not limit the number of input colors for replacement. Changing a target alpha to 00 instead can remove opaque artwork, so arbitrary RGBA maps do not preserve silhouettes.

4. Separate the replacement from browser decoding

Before replacement, source-to-browser comparison found four changed positions: one hidden-RGB pixel and all three partial-alpha controls. No alpha or opaque-RGBA values changed at that stage. The exported PNG then matched the exact replacement of the independent window OffscreenCanvas readback across all four channels.

“Unchanged” therefore refers to the decoded input, not every original-file channel byte. The independent probe used the production decoder’s bitmap options and canvas sequence in a window; it was not an instrumented raw capture inside the production worker. Keep the original when hidden or partially transparent RGB values are significant.

Sources and next steps