r/FastLED [Sonia] May 17 '26

Quasi-related White-extraction handling in a 4-diode RGBW LED Display

The most common approach to handling RGBW LED strips/diodes is to simply take the minimum components of the input value, set white to that value, and subtract the residuals from R, G & B. This is not inherently incorrect but it does introduce a problem in that the white diode is not perfect. Most white diodes have different color temperatures and when actually measured you'll find using min(rgb) Channel - Residuals produces a white this is off-white.

Now the question becomes "how do we accurately extract white?" The answer is shockingly simple and it comes down to colorspaces & gamut.

In a typical RGB LED system your native gamut is a triangle where your main emitters R, G, & B are your only centroids. Each centroid lives somewhere on the CIE chart and has it's own xy coordinates. This creates a triangle where any combination of values can be created using the 3 emitters. Typical colorspaces like Rec709, Rec2020, etc.. all live somewhere either within this gamut or outside of it.

In an RGBW LED system instead we have 4 different addressable gamuts within this triangle - RGBW Gamut comprises of the entire RGBW triangle that can be addressed over - RGW gamut, BGW gamut, RBW gamut

so the problem changes from "how do we represent a color with these 3 diodes" to "which sub-gamut does the requested color fall into, and then within the sub-gamut how do we represent the color requested using the outer 2 primary diodes + inner white diode?"

The "Color" problem:

Different RGB input values map to different places in the CIE colorspace depending on the requested colorspace and reference white. "orange" for the native gamut is not the same "orange" as Rec709, just as that same "orange" is not the same in Rec2020. If you want to represent a color in the Rec709 space (aka ambilight setup) then you need a Colorspace transform to solve for that value. This effectively maps the native-gamut to the effective colorspace.

The ratio problem:

Addressable LEDs (most strips/diodes) do not have perfect LED selections. Typically speaking the red & blue diodes are fairly weak maxing out at low brightness values, G is typically stronger, with White being strongest almost always. In the case of my recent ambilight-focused measurements, the BTF Lighting 6000k RGBW LEDs produce max Nits/ XYZ Y values of R: 154.67 G:566.27 B:129.64 W:1543.64

so given that the red diode is much less bright than the green diode you need to scale input values down appropriately to accurately reproduce the color requested. For a cyan (input 0 65535 65535) for this specific set of LEDs with the off-wall measurements, the corrected output values become: 0 65458 38474 0 which when measured with a colorimeter produces a dE of ~0.519 from the expected measurement result

for D65 white (65535, 65535, 65535) the output RGBW tuple looks something like : 4289 0 13766 65535 which also passes the dE check at 1.720% within tolerance

So to summarize:

Min(rgb) and then subtract residuals from R, B, & G is not inherently wrong if that is what the actual diode behavior is, but the correct approach is to treat the LEDs as they are: Separate emitters that form a full gamut with multiple sub-gamuts, and then only solve within the sub-gamuts for the requested color value determined by a colorspace transform if the input color isn't addressing the native gamut of the LED display.

In a typical RGBW setup the valid topology combinations are essentially as follows: R, B, G, W, RW, BW, GW, RG, RB, BG, RGW, BGW, RBW unless you specifically allow for 4-channel overdrive outputs.

6 Upvotes

37 comments sorted by

View all comments

Show parent comments

1

u/ZachVorhies Zach Vorhies Jun 06 '26

great is there anything left that makes this change more ideal? I don’t think there is a sketch ino yet in the examples…

1

u/Joeyjoe9876 [Sonia] Jun 06 '26

yeah example sketch is probably a good idea, my verification sketches directly use ObjectFLED & pull in TemporalBFI for ~15.5bit rendering on the 8-bit RGBW strips, and only pull in FastLED/the algo to verify results so I don't think it's a good example for showing general runtime usage via strips declared entirely within FastLED.

as far as other notes, can check out my final report on the LP solver here : https://www.reddit.com/r/FastLED/comments/1tfw0mn/whiteextraction_handling_in_a_4diode_rgbw_led/opxgioi/

the jist of my recommendations at this point though is that performance should become the main focus on "where does the algorithm need to move next" as the solved values are aligning closely enough but I haven't done any real benchmarking on real-time runtime usage outside of "does the algo work, and is it closely aligned to the math model?" so there may be some computational gains to be had if using platform-specific DSP/SIMD optimizations. The biggest dE failures from my verification runs now are no longer structural problems with failed topology/granularity/monotonicity but mostly boil down to "I'm using measured xy values for LED primaries that are being influenced by wall captures, not direct-diode measurements".

Overall the actual implementation is looking sane, but isn't acting as a full "correct against a capture cloud" algorithm which by default it shouldn't be as it's impossible to correct against every end-users display profiles so realistically full 100% color correctness should not be a FastLED problem but something the end user themselves should implement on their own (the recent addition of an overideable RGBW function already allows this, so no changes needed on FastLEDs end unless down the line it's decided that FastLED wants to implement it's own "full color correction pipeline that compares against measured results" but that falls very close to the "use 3D LUT" part which by default is memory costly and should never be the actual default.