ColorTool - A module for interpolating color the right way!

What if I told you that you are getting the wrong color values from Color3:lerp()?

After working on the design for a system in my game, I realized that something was wrong with my color transitions, namely that the brightness really seemed to tank at 50% between my two colors. Turns out this is an artifact with how color is stored!

If you are interested in what causes this issue as well as how to fix it, MinutePhysics has an outstanding video on the topic. See it here!

Computer Color is Broken - YouTube

In response to this issue I designed a super simple module that offers a function to properly transition between colors as well as a function to get grayscale colors with this method.

Click here to see an example of color interpolation from red to green.

TOP: Roblox stock method

BOTTOM: My module’s method, gamma = 2.2

Notice the brown color in the center of Roblox’s stock method.

Here’s the module:

(This is not able to be required via its ID. You should insert it into your place.)

Here’s the API:

Color3 LerpColor (Color3 colorA, Color3 colorB, number fraction, number gamma = 2.0)

This function takes in two colors and a fraction similarly to lerp(), and functions identically. You can optionally specify the gamma value. Gamma values generally range from 1.8 to 2.2 in most devices. It defaults to 2.0, and this value should work just fine.

number GetGrayscale(Color3 color, number gamma = 2.0)

This function returns a number ranging from [0,1] representing the average of R, G, and B based on gamma. Gamma values generally range from 1.8 to 2.2 in most devices. It defaults to 2.0, and this value should work just fine.

31 Likes

Why wouldn’t you just lerp in HSV space if you needed that kind of transition? This still seems to have a brightness drop in the middle between red and green, it never becomes a nice kind of yellow. Lerping in HSV should result in a linear change of brightness, not one that drops in between, but perhaps that’s the kind of effect you were intentionally looking for.

6 Likes

That’s certainly a good point. I may implement that feature into the module as it is quite handy in general.

The RGB^2 was indeed a desired action, one primary reason being the gamma control among other benefits mentioned within the video I linked. It works great on neon because you can lower the gamma to control how much of the color bleeds over for bright colors, effectively balancing out the brightness of a bunch of neon parts (For instance, if I were to make all of those parts neon, the yellow would have strong influence over the red/green and therefore lowering it to be below normal would balance that out)

HSV certainly would not result in a “linear change of brightness”. The V, which is the Value or Luminosity, is equivalent to the highest RGB value. In fact, you could use HSV as a math.max function for 3 numbers within the domain of [0, 1]:

--- Takes in 3 parameters, between 0 and 1 (inclusive) and returns the highest
local function max(a, b, c)
	local _, _, value = Color3.toHSV(Color3.new(a, b, c))
	return value
end

print(max(0.5, 0.3, 0.2)) -- 0.5

RGB is not a visually linear color space, and because HSV is based on RBG, it isn’t either. There are tons of color spaces out there which organize the colors in different places along different axis, with varying numbers of dimensions. There has been a considerable amount of research done on this matter, and there are a number of color spaces which are pretty good when it comes to being visually linear. The one I personally prefer is the CIELAB/CIELUV color space. Fractality has an anim module with this capability, available here. I have a Tween module which has this in its Lerps definition as well, available here, installable via RoStrap. I have a small webpage on this here.

3 Likes

Yes, so if you lerp from one set of HSV values to another, the change in each component will be linear, and you will see red - yellow - green in the example given in OP. The V changes linearly.

HSV lerping leads to more natural color transitions, and I suggested this one because Roblox already provides methods to go between RGB and HSV.

CIELAB/CIELUV might be great but there is no built-in stuff for CIELAB/CIELUV, and the improvement in color transitions is probably minor compared to the improvement between lerping in RGB and in HSV. That’s why I suggested HSV rather than anything more complex, not because I was trying to note the best way ever to implement it, but because it’s the easiest to get going. (I don’t assume anything about OP’s proficiency)

4 Likes