Search code examples
javascriptledlight

RGB to real-life light colour Javascript conversion


I've got what I think is quite an interesting problem that needs an elegant solution...

I have an RGB value, for example 205,50,63.

I am trying to simulate the colour of an RGB LED on a webpage as if it were REAL-LIFE LIGHT.

For example, the RGB colour 255,0,0 would display as red, both on the LED and on the webpage.

Likewise, the RGB colour 255,255,255 would display as white, both on the LED and on the webpage.

BUT the RGB colour 0,0,0 would display as off on the LED and would be displayed as black on the webpage.

What I am trying to achieve is that both 0,0,0 and 255,255,255 display as white. As if the dimmer the LED is, the whiter it gets.

Ive been trying to apply a proportional algorithm to the values and then layer <div> over the top of each other with no luck. Any thoughts?


Solution

  • I'm not sure what the case you're imagining is, but reading your desired output, what is wrong with simply scaling up so the maximum value becomes 255?

    function scaleUp(rgb) {
        let max = Math.max(rgb.r, rgb.g, rgb.b);
        if (!max) { // 0 or NaN
            return {r: 255, g: 255, b: 255};
        }
        let factor = 255 / max;
        return {
            r: factor * rgb.r,
            g: factor * rgb.g,
            b: factor * rgb.b,
        };
    }
    

    So you would get results like

    scaleUp({r: 0, g: 0, b: 0}); // {r: 255, g: 255, b: 255}
    scaleUp({r: 255, g: 0, b: 0}); // {r: 255, g: 0, b: 0}
    scaleUp({r: 50, g: 80, b: 66}); // {r: 159.375, g: 255, b: 210.375}
    

    Notice this collapses all {x, 0, 0} to {255, 0, 0}, meaning {1, 0, 0} is vastly different to {1, 1, 1}. If this is not desirable you'd need to consider special handling of such cases


    More RGB hints; you get smoother "more natural" light transitions etc if you square and root around your op, e.g. rather than x + y, do sqrt(x*x + y*y)

    This leads to a different idea of how to solve the problem; adding white and scaling down

    function scaleDown(rgb) {
        let whiteAdded = {
            r: Math.sqrt(255 * 255 + rgb.r * rgb.r),
            g: Math.sqrt(255 * 255 + rgb.g * rgb.g),
            b: Math.sqrt(255 * 255 + rgb.b * rgb.b)
        };
        return scaleUp(whiteAdded);
    }
    

    This time

    scaleDown({r: 0, g: 0, b: 0}); // {r: 255, g: 255, b: 255}
    scaleDown({r: 255, g: 0, b: 0}); // {r: 255, g: 180.3122292025696, b: 180.3122292025696}
    scaleDown({r: 50, g: 80, b: 66}); // {r: 247.94043129928136, g: 255, b: 251.32479296236951}
    

    and have less of a jump around edge points, e.g.

    scaleDown({r: 1, g: 0, b: 0}); // {r: 255, g: 254.99803923830171, b: 254.99803923830171}
    

    Finally, notice this maps rgb onto the the range 180..255, so you could transform this to 0..255 if you want to preserve your "true red"s etc

    function solution(rgb) {
        let high = scaleDown(rgb);
        return {
            r: 3.4 * (high.r - 180),
            g: 3.4 * (high.g - 180),
            b: 3.4 * (high.b - 180),
        };
    }
    

    So

    solution({r: 255, g: 0, b: 0}); // {r: 255, g: 1.0615792887366295, b: 1.0615792887366295}
    solution({r: 1, g: 0, b: 0}); // {r: 255, g: 254.99333341022583, b: 254.99333341022583}
    solution({r: 50, g: 80, b: 66}); // {r: 230.9974664175566, g: 255, b: 242.50429607205635}