Texture Recolor

11 min read

Remaps a texture from one palette to another in perceptual space, the way pixel-art tooling re-skins a sprite for a new biome or a new faction without losing its silhouette.

The Recolor tab is Chroma’s palette-driven texture remap workspace. Three slots (source texture, source palette, target palette), one button. Every pixel of the source texture finds its nearest match in the source palette by Oklab (a perceptual color space where equal numeric distances correspond to equal-looking color differences) delta-E and is rewritten with the corresponding entry from the target palette. The output is a brand-new PNG asset; the source texture is never modified.

Summary

The case where a texture exists in one palette and needs to exist in another is common across game art pipelines. A pixel-art character sprite needs alternate-color variants for player customisation, faction allegiance, or boss-vs-minion identification. A UI tile sheet needs a dark-theme version. A tileset needs a winter-season recolor. Doing this by hand in an image editor is mechanical, slow, and error-prone (any pixel that drifts a single value off the source palette gets stranded in the recolor); doing it programmatically with naive equality fails on JPG sources, anti-aliased edges, and anything that was not authored in strictly indexed color.

The Recolor tab handles the case by perceptual proximity rather than by exact equality. For every pixel in the source, find the nearest entry in the source palette by Oklab delta-E, then look up the corresponding entry in the target palette (same index) and write that color. The result is robust against anti-aliased edges and minor compression artefacts; the result is also obviously dependent on the source palette being a reasonable approximation of the colors actually present in the source texture.

  • Generate alternate-color variants of pixel-art sprites, character costumes, and tile sheets from a single source palette plus N target palettes.
  • Build seasonal or theme variants of a UI tile sheet by recoloring against a target theme palette.
  • Reproduce a faction or alignment palette across an entire prop set in one batched pass.
  • Verify that two palettes are interchangeable before committing to a recolor by previewing in the Compare tab first.

Features

  • Three input slots. Source Texture (Texture2D), Source Palette (ColorPaletteAsset), Target Palette (ColorPaletteAsset). Drop or pick into each.
  • Recolor and Save… button. Validates the three inputs, opens a Save As panel, runs the remap, writes the result as a PNG, imports the new asset, pings it in the Project window, and updates the status label below the button.
  • Equal-entry-count constraint. Source and target palettes must have the same number of entries. The button surfaces a “Palette Size Mismatch” dialog when they do not, with the actual counts spelled out.
  • Read/Write requirement. The source texture must have Read/Write enabled in its import settings (otherwise pixel-level access is blocked). The button surfaces an explicit “Not Readable” dialog when the source texture is unreadable.
  • Oklab nearest matching. Pixels are matched to the source palette by Euclidean distance in Oklab perceptual space, not by RGB equality. Source-palette Oklab coordinates are precomputed before the per-pixel loop so the inner-loop work is just distance math.
  • Alpha preserved. Each output pixel takes its color from the target palette and its alpha from the source pixel. Transparent and semi-transparent areas survive the remap unchanged.
  • Default output naming. The Save As panel pre-fills the filename as {sourceName}_Recolored.png and defaults the directory to the source texture’s directory (or Assets/ when the source has no asset path, e.g. an imported-from-disk texture).
  • Non-destructive. The source texture is never modified. The remap reads source.GetPixels(), computes a new pixel array, encodes it as PNG bytes, and writes a new asset. The new texture is a regular Texture2D import that the project treats like any other.
  • Drag-drop into the palette slots. Both palette fields accept drops from the library sidebar, the Project window, and external files (the standard Chroma palette drop semantics).

Recipes

These recipes are self-contained: each one answers a single authoring problem. Scan the names, jump to the one that matches your situation, and follow the steps.

Recolor a sprite to a target palette

Problem. You have a pixel-art sprite sheet authored in one palette and you need a variant in a different palette – a faction swap, a seasonal reskin, a dark-theme UI sheet, or a boss-vs-minion distinction. You want the output in seconds, not an afternoon in an image editor.

Solution. Assign all three slots and click the button:

  1. Drag your Texture2D from the Project window onto the Source Texture field, or click the picker icon.

    The source texture must have Read/Write Enabled turned on in its import settings (Project > Texture import > Advanced > Read/Write Enabled). Without that flag, Unity does not keep a CPU-side copy of the pixel data and Texture2D.GetPixels() returns nothing. If Read/Write is off, the Recolor and Save… button shows a “Not Readable” dialog; the fix is to flip the flag in the texture’s importer.

  2. Drop the palette the texture was authored in onto the Source Palette field. This is what each pixel gets matched against, not what the output should look like. For a 16-color sprite sheet, this should be the literal 16-color palette the artist used.

  3. Drop the palette you want the output to use onto the Target Palette field. The two palettes must have the same number of entries. Entry 0 in the source maps to entry 0 in the target, entry 1 to entry 1, and so on – the positional correspondence is how the remap knows which target color replaces which source color.

  4. Click Recolor and Save…. The validation runs in order: all three slots assigned, palette sizes match, source texture readable. Any failure shows a dialog and stops before the Save As panel opens.

  5. In the Save As panel, the filename pre-fills as {sourceTextureName}_Recolored.png and the directory defaults to the source texture’s folder (or Assets/ for textures with no project path). Edit both as needed, then confirm.

    For batched workflows where one source maps to several target palettes, rename the output before saving – Hero_Sprite_BlueTeam.png is easier to manage than four files all named Hero_Sprite_Recolored.png.

  6. The new PNG imports automatically. The Project window pings the new asset, the Inspector shows it, and the status label reports the output path.

Build a source palette from an existing texture

Problem. You want to recolor a texture but you do not have a ColorPaletteAsset that represents its colors – maybe it was painted by hand, exported from an image editor without an indexed-color palette, or the original palette asset is lost.

Solution. Use the Image Sampler tab to extract the palette automatically, then refine it in the Palette Editor if needed:

  1. Open the Image Sampler tab and load the source texture via Load Image or by dragging it into the preview.

  2. In the Extract section, choose Median Cut at the same color count as the original palette (for NES-palette-style pixel art that is 4, 16, or 25 colors; for a loosely painted sprite it may take a few tries to find the right count). Median Cut recovers hard color boundaries cleanly, which is exactly what indexed-color pixel art has.

    For textures that were painted with gradients or transitions rather than strict flat color, try K-Means instead. K-Means clusters around the most representative centroid colors, which gives a better source palette when the texture has soft edges or was authored with anti-aliasing.

  3. Save the extracted palette as a ColorPaletteAsset using the Create Palette button in the Image Sampler.

  4. Open the saved palette in the Palette Editor. If any entries look wrong (a dark shadow tone landing on the wrong cluster, a skin highlight merging with a background color), nudge the swatch manually. The Palette Editor’s per-entry color picker and the Compare tab’s nearest-Oklab stats make it easy to spot outliers.

  5. Return to the Recolor tab, assign your refined palette to Source Palette, and proceed with the recolor.

Author a matching target palette

Problem. You want to create a new faction palette, a winter-reskin palette, or a dark-theme variant that maps cleanly to an existing source palette – entry counts must match and the positional correspondence must be deliberate.

Solution. The safest starting point is to duplicate the source palette and edit the swatches:

  1. Find the source ColorPaletteAsset in the Project window. Right-click it and choose Duplicate (or Ctrl+D / Cmd+D), then rename the duplicate to something that identifies its purpose (e.g. CharacterPalette_RedTeam).

  2. Open the duplicate in the Palette Editor. The entry count is already correct and the positional correspondence is already intact: entry 0 of the target is the one that will replace entry 0 of the source.

  3. Edit each swatch to the target color. The Compare tab’s two-palette view is useful here: load the source and the target side by side and check the nearest-Oklab distance between paired entries. A very high distance on one entry pair flags an accidental replacement (an entry that got dragged to the wrong position) before the recolor runs.

  4. Return to the Recolor tab, assign the edited duplicate as Target Palette, and run the recolor.

Diagnose artifacts in a recolored texture

Problem. You ran the recolor and the output has banding, mismatched edges, wrong colors in specific areas, or regions that look like they snapped to the wrong palette entry.

Solution. The remap matches every pixel to the nearest source-palette entry in Oklab space. When the result looks wrong, the cause is almost always one of three things:

The source palette is too small for the texture’s actual color range. Anti-aliased edges, gradient regions, and blended shadows all contain colors that sit between the palette entries. Every such pixel snaps to its closest source-palette entry, which can produce visible banding in gradients or wrong colors at silhouette edges. The fix is to extend the source palette to cover the missing colors:

  • Run the Image Sampler Extract at a higher color count and compare the result against your current palette in the Compare tab.
  • Identify the color ranges that have no close palette entry (high nearest-Oklab distances flag them).
  • Add those colors to the source palette in the Palette Editor, then add matching entries to the target palette in the same positions, and rerun.

A specific color has no good match in the source palette. A particular hue (often a specular highlight, a rim light, or a shadow tone the original palette did not anticipate) has no close entry and snaps to whichever source-palette color is least wrong. The output pixel reads as obviously incorrect. Same fix: extend the source palette to cover that color, add the corresponding target entry, rerun.

A target palette entry drifts too far from its source counterpart. A source entry that is light blue mapped to a target entry that is dark green will make every light-blue pixel in the source read as dark green in the output – which is sometimes correct (a faction recolor that shifts blue to red by design) and sometimes accidental (an entry got dragged into the wrong position in the Palette Editor). Open both palettes in the Compare tab. The nearest-Oklab stat for each paired entry surfaces the outlier immediately.

Tips and pitfalls

  • The source palette slot is for the palette the texture was painted in, not the desired output. Putting the target palette in the Source Palette slot and vice versa gives you colors mapped in the wrong direction, which produces garbage output. Source palette = what is in the texture now. Target palette = what you want it to look like.
  • The source and target palettes must be the same length. This is enforced with an explicit dialog that shows both counts. The constraint exists because the remap relies on positional index correspondence; there is no fuzzy matching between source and target entries.
  • Read/Write must be enabled on the source texture. This is a Unity import setting on the source asset, not a Chroma setting. Toggle it in the texture’s importer, then re-run; the recolor cannot proceed without it.
  • The remap matches by Oklab proximity, not by exact equality. A texture with anti-aliased edges, JPG compression artefacts, or hand-painted gradients still remaps cleanly because every pixel finds some nearest source-palette entry. Whether the result reads well depends on how representative the source palette is of the texture’s actual colors.
  • The output PNG carries the source’s alpha channel. Transparent areas remain transparent, semi-transparent areas remain at their original alpha. The remap only changes RGB.
  • The Recolor tab is read-only on all inputs. Both palette assets and the source texture are referenced but never modified. The only write is the new PNG.