Fixing Incorrect Colors in an Emulator Without Hiding the Cause with a Shader
A layer-by-layer color diagnostic for emulators covering source revision, palette RAM, pixel formats, endianness, color management, shaders, and captures.
Wrong emulator colors can originate in emulated palette logic, a game/region mismatch, GPU pixel-format conversion, host color management, HDR, or a shader. Adding a color-correction shader first may make one screenshot look familiar while masking a swapped channel or incorrect palette index. Capture the pipeline at successive boundaries and find the first value that diverges.
Establish a neutral baseline
Record emulator/core version, game hash and region, firmware hash, renderer, output color space, display/HDR mode, and all video overrides. Reset only the video profile to documented defaults, then disable shaders, texture packs, post-processing, HDR, night-shift filters, and per-game color corrections. Preserve the original configuration for rollback.
Use a known diagnostic ROM or deterministic scene with documented palette values. Compare an emulator internal screenshot and an external camera capture separately. A screenshot taken before OS/display processing can be correct while the monitor image is wrong; a camera adds its own white balance and exposure.
Inspect indexed color at the source
Many consoles store palette indexes or compact RGB words, not display-ready sRGB pixels. Verify the emulated write reaches the correct palette RAM address with correct masking, mirroring, and timing. Log the raw register value and selected palette entry around the bad pixel.
For a packed format, make extraction explicit. A 15-bit word might assign five bits per channel, but platform order and unused/brightness bits differ:
r5 = (word >> R_SHIFT) & 0x1f
r8 = (r5 << 3) | (r5 >> 2)
That bit-replication expansion maps endpoints correctly; a simple left shift leaves the maximum at 248. Verify byte order before bit order. Red/blue swaps often indicate BGRA/RGBA disagreement or endian conversion, while globally dark output can indicate range or transfer-function confusion.
Some hardware, notably composite-output systems, does not define a single digital RGB palette. Color depends on signal phase, decoder, region, and display. In that case several documented palettes can be legitimate, but the emulator should identify the model rather than label one subjective table “hardware accurate.”
Trace the renderer and host pipeline
Dump the emulator’s pre-render framebuffer and compare it to the uploaded GPU texture. Confirm row pitch, channel layout, alpha interpretation, integer normalization, and texture/view format. Then compare software and hardware renderers. If software is correct and one GPU path is wrong, the emulated palette is probably not the defect.
Check limited versus full range, sRGB versus linear sampling, double gamma, ICC profile, HDR tone mapping, and OS accessibility filters. Shaders should declare their expected input transfer function. Applying an sRGB transform twice changes midtones even when channel values look plausible.
Fix one layer and lock it with tests
Correct the register mask, lookup table, pixel-format declaration, shader transfer function, or host output setting shown by evidence. Re-enable enhancements one at a time. Test region variants, fades, transparency, emphasis/brightness bits, save-state restore, screenshots, fullscreen/windowed, SDR/HDR, and multiple renderers.
Add a deterministic framebuffer hash or pixel sample test before host color management, plus a rendered image comparison with tolerances where GPUs differ. The right fix restores the first incorrect boundary; shaders can then remain optional artistic or signal-simulation choices instead of concealer over an emulation bug.
Related:
- Fixing Emulator Save Files Whose In-Game Clock Drifts or Resets
- High-Level vs. Low-Level Emulation: Replacing Behavior or Reproducing Firmware
Sources: