Sprite Palette Swap — Recolour Ramps and Team Colours
Every recolour here is a palette transform: pixels keep their palette index, so two frames that shared a colour still share it and the darkest shade stays the darkest.
A palette swap recolours a sprite by mapping each colour of one shading ramp onto the step in the same position of another ramp: the darkest red of a cloak becomes the darkest blue, the highlight becomes the highlight, and nothing else changes. In the Pixel Lab's Recolour stage you select the source colours, generate a target ramp from one base colour (or pick one), and apply it to every frame; team variants (red, blue, green, yellow or any #RRGGBB) export as one ZIP with a folder and a .gpl per variant.

- PNG framesMeasured· Pillow
- PaletteMeasured· re-import
- pixel-lab.jsonUNVERIFIED
Map by position in the ramp, never by nearest colour
Replacing each colour by its nearest match in the new palette breaks shading: two neighbouring shades of red can both land on the same blue, and a light red can land on a darker blue than its shadow. The Lab sorts the selected source colours by Oklab lightness and maps them onto the target ramp by position, so the first becomes the darkest target, the last the lightest, and the order cannot invert.
A target ramp can be generated from a single base colour. It keeps the source ramp's lightness steps, takes the base colour's hue and chroma (so the base's own lightness is not used), boosts chroma in the mid-tones and rotates hue by up to 12° across the ramp, darker end one way and lighter end the other. The result is a starting point to adjust, not a finished art decision.
Every recolour is a palette transform: a pixel keeps its palette index and only the colour behind the index changes. Two frames that shared a shade still share it, which is why the swap cannot introduce flicker or stray colours between frames.
- Ramp swap
- Mapping selected source colours onto a target ramp by their position from dark to light.
- Team variant
- The same sprite recoloured with a different base colour for each team or player.
- Hue window
- A range of hues (centre ± degrees) whose colours are replaced while keeping their own lightness.
- Status tint
- A recipe that pulls every colour toward one hue (frozen, poison, burn, ghost, damage flash).
Example: a red cloak ramp turned into team blue
Computed with the Lab's own functions (L = Oklab lightness, h = hue):
source (red cloak) #5a1e28 L0.33 h13 | #a02c3a L0.48 h18 | #dc5a50 L0.63 h27 | #f5a08c L0.79 h34
base: team blue #3467d6 (L0.54 is ignored; hue 263°, chroma 0.18 are used)
generated ramp #002d8c L0.35 | #004ed5 L0.48 | #4f7bff L0.62 | #99acff L0.76
hue along the ramp 262° → 262° → 267° → 273°
mapping by position darkest→darkest … lightest→lightest; black and white untouched
3 colours onto a 5-step ramp: positions 0, 0.5, 1 → steps 0, 2, 4
test: 4 variants × 8 frames = 32 PNGs; 3 selected slots changed, 13 byte-identicalThe generated lightness follows the source within 0.03; the lightest blue lands a little lower because sRGB cannot hold that blue at full chroma. Nudge it by hand if the highlight looks dull.
What it does
- Ramp swap: selected source colours map onto a target ramp by position in lightness, never by nearest colour.
- Auto ramp from one base colour, keeping the source ramp's lightness steps.
- Team colour batch: N variants (red, blue, green … or any #RRGGBB) exported as one ZIP with a folder and a .gpl per variant.
- Hue-range replace and status presets (frozen, poison, burn, ghost, damage flash), labelled as recipes.
How to do it
- Drop the frames.
- Pick the colours of the ramp to change on the palette.
- Choose a ramp, base colour, hue window or status preset and preview it.
- Apply, or export the team variants.
Check a swap
- Before applying, compare the source and target swatches side by side: both rows should run dark to light.
- Step through the frames: every frame should change only the selected colours, and outlines, skin and eyes stay as they were.
- Open one variant's .gpl: the untouched slots have the original colours in the original order.
- Put the variants next to each other at 1×: the teams should be distinguishable at game size, not only zoomed in.
Troubleshooting
| Symptom | Likely cause | How to check | Fix |
|---|---|---|---|
| The shading came out inverted or flat | A colour outside the ramp (outline, skin) was selected with it | The selected swatches do not form one dark-to-light sequence | Select only the ramp's colours; the order you click in does not matter, lightness decides |
| Other parts changed colour too | The cloak shares a palette entry with another part of the sprite | Select that swatch and compare its pixel count with the cloak alone: other parts use it too | Give that part its own colour first (for example in the Studio's Pixel workspace), then swap |
| The team colour is much darker or lighter than the base I picked | The generated ramp keeps the source lightness steps and uses only the base's hue and chroma | Compare the base swatch with the middle of the generated ramp | Pick a target ramp by hand, or choose a base with the hue you want and adjust the steps |
| The hue window catches too much or nothing | The window (± degrees) is too wide or too narrow, and near-grey colours are ignored | The preview marks the matching colours with a tick | Narrow or widen the window; greys need the ramp swap instead |
Other ways to do it
| Option | When it is the better choice |
|---|---|
| Team-colour variants in the Studio's Pixel workspace | The sprite has layers and tags: variants apply to every frame and layer and each becomes a new sprite; in an indexed sprite a single palette edit recolours every frame. See the palette editor. |
| One-colour palette swap with a tolerance | You only need to replace a single colour, with a shading offset: palette swap. |
| Aseprite: Edit › Replace Color | You work in Aseprite and replace colours one at a time, with a tolerance, on the selection or on all cels. |
Outputs and how each was checked
"Measured" means the downloaded file was opened again outside the page (Pillow, numpy or an independent parser) and the property was measured. Loading these files in a game engine was not tested.
| Target | Files | Check |
|---|---|---|
| PNG frames | anim_N.png · 1×–8× nearest | Measured PillowEvery exported colour re-read from the PNGs lies inside the locked palette. |
| Palette | .gpl · .hex (Lospec) · .json | Measured re-importEach format was exported and imported back through the UI: 16 → 16 colours. |
| pixel-lab.json | generic frames + palette | UNVERIFIEDGeneric JSON; not loaded in any engine. |
Evidence
8 anti-aliased frames → one 16-colour palette with 0 exported colours outside it; an unchanged region identical in all 8 frames with dithering off and with Bayer 4×4; the anti-alias remover took 271 colours to 16 without moving a single silhouette pixel; integer scale, offset and 2.5× non-integer scaling detected exactly.
Limits
- Variants export as PNG frames and .gpl files, not as .aseprite.
- One palette for all loaded frames: a sheet with two unrelated characters must be split first.
- Status presets are recipes, not authored art.
- Frames larger than 4096 × 4096 are refused: every pass runs on the main thread, and there is no cancel button yet.
- A sprite sheet is treated as one image; cut it into frames first (sprite sheet cutter).
- With a dithering mode on, the cleanup counts include dither pixels; set dithering to None first.
- The exported JSON is generic; importing it into Godot or Unity was not tested.
Versions and sources
Nerulio: docs/PIXEL-LAB.md (Chromium; 4 team variants × 8 frames exported as 32 PNGs in 4 folders, each changing exactly the 3 selected palette slots and leaving the other 13 byte-identical). The ramp above was computed with src/game/palette.js. Aseprite's Replace Color follows its documentation.
Official documentation used
Questions
Will shading stay consistent across frames?
Yes. Recolouring rewrites the palette, not pixels, so every frame keeps the same index for the same shade; in the test, 4 variants changed exactly the 3 selected slots and left the other 13 byte-identical.
Is there a one-colour swap with tolerance?
Yes, the separate palette swap tool does a single colour with a tolerance and a shading offset.
Is it free, and do I need an account?
Everything on these pages is free and works without an account. There is no watermark and no export that is held back.
Are my sprites uploaded?
No. The Studio decodes, packs and exports in this browser tab. Projects are autosaved in the browser (IndexedDB) and can be saved as a .nerulio file.
Does it need Aseprite or an engine plugin installed?
No. .aseprite files are read and written in the browser, and each engine bundle carries what that engine needs (for Godot, the import settings and scene; for Unity and the Godot TileSet, a small editor script).