Integer (Hex) Support for Color3

Problem

It is extremely common to represent colors as hex triplets (eg. 0xFF5500 or #FF5500) - which are just integers, but on Roblox it is tedious to convert an integer to a Color3, or a Color3 to an integer.

There is no easy way to construct a new Color3 from an integer without code in the middle that extracts each RGB component via bit shifting or some other operation. The same can be said for conversion of Color3 RGB components back into an integer.

It shouldn't be necessary for us to write code to use such a common color representation.

Solution

  • A new property or function for Color3 instances that provides access to the color the instance represents as an integer.
  • A new constructor that accepts an integer.
  • Eg.
    Color3.toInt()
    Color3.fromInt(0xFF5500)

    Use Cases

  • Encoding and decoding Color3 instances for datastores.
  • Conversion of user-input hex codes (eg. color picker UI) into Color3.
  • The ability to use colors as unique dictionary keys - constructing a new Color3 to index the dictionary won't work if keys are Color3 instances because the object reference will be different. However, an integer would work fine, could be saved in a datastore, and could easily be converted back into a Color3. (This is specifically my use case)
  • 59 Likes

    Sounds pretty logical. I remember Sorcus said something about implementing CSS or some Roblox version of that into the engine, so you would assume Color3 hex conversion may be in the pipeline already.

    3 Likes

    Would be very helpful

    2 Likes

    Here is code to change html to color3 that ive messed around with in the past:

    -- html color codes to color3 lua
    local function hex2rgb(hex)
    	hex = hex:gsub("#","")
    	local r = tonumber("0x"..hex:sub(1,2))
    	local g = tonumber("0x"..hex:sub(3,4))
    	local b = tonumber("0x"..hex:sub(5,6))
    	return Color3.fromRGB(r,g,b)
    end
    print(hex2rgb("#ff0000")) --> 1,0,0
    print(BrickColor.new(hex2rgb("#ff0000")).Name) --> Really red
    

    But I agree, there should be another Color3 built-in that does this for us. Both Converting to a color3 and color3 to this format.

    9 Likes

    You can also do this just by multiplying by 255 and adding the numbers together (it’s a bit more than that, but the idea is replicating the bitmath).

    Here’s a quick hack while you wait for this to be added.

    local Color3 = setmetatable({
    	fromInt = function(i)
    		return Color3.fromRGB(math.floor(i / 65536) % 256, math.floor(i / 256) % 256, i % 256)
    	end
    }, {__index = Color3})
    
    
    local test = Color3.fromInt(0x00A2FF) -- Roblox Blue
    print(test)
    
    -->> 0, 0.635294, 1
    
    16 Likes

    Agreed.

    You can actually paste in hex color values into Color3 fields in Studio and it will work.

    5 Likes
    local floor = math.floor
    local fromInt = function(int)
        return Color3.fromRGB(floor(int/256^2)%256,floor(int/256)%256,floor(int)%256)
    end
    local toInt = function(col)
        return floor(col.r*255)*256^2+floor(col.g*255)*256+floor(col.b*255)
    end
    3 Likes

    I ran into a need for this again yesterday while refactoring some code.

    I needed to keep a table indexed by color so I could keep track of a bunch of parts that were the same color. For reasons stated above (Color3.new returns a different reference that cannot be used to access the desired index in the table), I cannot do this using Color3 instances. This table also needed to be sent over a remote, which of course also wouldn’t work: Cannot convert mixed or non-array tables: keys must be strings.

    Previously I was using BrickColor and color names, but this breaks down once I start trying to support bricks with Color3 set instead of BrickColor, because if two colors I’m trying to store in the table are too similar, they get mashed into the same BrickColor index and the world burns down and I am sad.

    Of course this can and was solved by creating a utility module for converting between integer and Color3 in ReplicatedStorage, but this is just another nugget of garbage that shouldn’t need to be necessary for something that’s fundamental to digital color representation.

    1 Like

    That’s my main use case for this. It seems like a problem that’s so common to not be addressed with something simple like this. Right now working with Color3s is extremely annoying since you need to save 3 separate numbers instead of a single string. Ugh.

    Sorry to bump this thread but, I ran into an issue that requires this feature to be present.

    I think that limiting Color3 conversion to RGB & HSV is not enough and for a developer… I find this to be incredibly restricting especially when industry standards are HEX-based and not Color3 or anything similar. Now that I come to think of it, I can’t think of a time or moment when I was designing a website, graphic, or related to where I’d need to substitute RGB with HEX.

    While hacky methods do exist (the ones above this post are quite nice) but, it doesn’t match/doesn’t equate to actual support for it. Writing a method just to add support for something that seems to be a standard for the rest of the industry except here (woohoo) is silly & doesn’t make much sense.

    This thread seems to be almost two years old and it’s a bit concerning when it hasn’t been given the proper attention that this thread/feature needs. What’s even more concerning is that if you paste the HEX code into the Color3 field via explorer, it seems to work. It appears that it’s already in the pipeline but, isn’t a Lua method.

    Ex: Color3.fromHex("FFFFFF") or Color3.fromHEX(0xFFFFFF)

    3 Likes

    As a Roblox developer, it is too difficult to convert a decimal into Color3 and back.

    The problem is that it is more efficient to store color as a single decimal in JSON, than three keys in a dictionary. Assume the following:

    Lets assume we have a green colour [color=“aaff00”]this[/color].

    In decimal, this has the value of 11206400. And in JSON, this would generally appear as

    {
      "color": 11206400
    }
    

    This would use less keys and less data when serialised over

    {
      "r":102,
      "g":0,
      "b":0
    }
    

    While yes, I’m aware there are ways to do this with the current implementation in Lua, the process is very clunky as there is no way to get the color as a hex value or a decimal. We can only get the value of the colour between 0 and 1.

    With a simple :fromDecimal() and :toDecimal (or toHex) function, we can create a system like this

    local data = HTTP:JSONEncode{
      color = Brick.Color:toDecimal()
    }
    
    7 Likes

    I understand the desire to have this integrated into the system– but this is quite simple to integrate into your own module. It’s not clunky at all even! Just a few calculations and you’re good to go. You’d create a module that basically compiles/decompiles Color3s/Hexadecimal values. To save you the hassle, I actually did this myself not too long ago.

    I wrote this code based on a post I found on good ol’ Stack Overflow (forum post can be found here)

    local Color = {};
    Color.__index = Color;
    
    function Color:toDecimal(Color3Value)
    	local r,g,b = Color3Value.r, Color3Value.g, Color3Value.b
    	-- Just a small check to ensure that our r,g,b values are in our 0-255 component range.
    	if (r < 1 and g < 1 and b < 1) and (r > 0 or g > 0 and b > 0) then
    		r *= 255
    		g *= 255
    		b *= 255
    	end
    	
    	return r*(256*256)+g*256+b -- 256 is used here because we want to keep the next component from overlapping the previous component.
    end
    
    function Color:toColor3(Decimal)
    	local b = Decimal % 256
    	local g_0 = (Decimal % 65536 - b)
    	local r_0 = Decimal - g_0 - b
    	local g = g_0 / 256
    	local r = r_0 / 65536
    	
    	print(("Conversion: Decimal %q to Color3 R: %q G: %q B: %q"):format(Decimal, r, g, b))
    	
    	return Color3.fromRGB(r,g,b)
    end
    
    return Color
    

    Feel free to use this.

    2 Likes

    The issue with adding such a function to the API is that unlike hex color code strings, which are a universally agreed upon format across many platforms, there’s no standard convention for exactly how a color should be converted to an integer. That’s why it makes more sense for this to be implemented as a Lua module.

    It would make more sense to ask for conversion to/from hex strings given your use case too: If you’re saving to JSON, a hex-string color is just as compact as a decimal ("#rrggbb" vs ddddddddd) and is more readable if you need to debug it.

    5 Likes