# HEX to RGB

I’ve been playing with WebGL recently. During this time, I found that I needed a way to convert HEX values to RGB and learnt of an interesting way to do that, which I took some time to understand and thought I’d write it down.

Here is the function, written in Typescript:

``````const hexToRgb = (hex: Hex): Rgb => {
const num = parseInt(hex, 16);
const r = num >> 16;
const g = (num >> 8) & 255;
const b = num & 255;

return [r, g, b];
};

hexToRgb('000000'); // [0, 0, 0]
hexToRgb('FFFFFF'); // [255, 255, 255]
hexToRgb('2EC4B6'); // [46, 196, 182]
``````

Note that that this is very basic for demonstration purposes. There’s a bunch of things you can do to improve this like removing a prepended hash character, handle alpha channels, shorthand HEX etc.

Ok let’s run through this step by step.

## Javascript Number Type

Heads up! It’s important to know that Javascript stores the number type as 64 bits. There’s some meta data in the first 12 bits (which I won’t get into). So this leaves 52 bits to actually store a value. So `8` in Javascript is stored like this:

``````[12-meta-bits-here]0000000000000000000000000000000000000000000000001000
``````

For the sake of this article, I will not be printing all these `0`s and will be keeping it to the nearest byte or whatever makes sense.

## Number Parsing

``````const num = parseInt(hex, 16);
``````

`parseInt` converts a string to a number. It is pretty commonly used in Javascript due to the dynamic typing of the language. Why would we want that? Well when we want to perform mathematical operations, we need the right type to do so. It’s usually done like this: `parseInt(value, 10)`.

But hold on, we’re passing in `16` here, not `10`! This number represents the base.

Decimal, what us humans use day to day, is base 10. But there are other ways to represent numbers! Such as bits (base 2), and hex (base 16).

There’s a lot to learn here, but as an example, the number `26` is represented as:

• `26` in base 10
• `11010` in base 2
• `1A` in base 16 (yep, we use letters A-F here to present larger numbers!)

It’s important to note that the value, when printed, will be base 10. This number won’t make much sense in this case and we don’t really care too much, because we will be performing bit operations.

## Getting the Red Channel

``````const r = num >> 16;
``````

The bitwise operator (`>>`/`<<`) shifts the bits of a number in a certain direction.

The number `8` is `00001000` in bits. So `8 >> 2` is equal to `00000010`, which in `base 10` is `2`!

So why are we shifting by `16`? Well each color channel takes up 8 bits of space. If our hex value is `2EC4B6`, in base 2 it would look like this: `001011101100010010110110`. Which is split up by the color channels: `00101110` (red) + `11000100` (green) + `10110110` (blue).

Due to this, our red and blue channels are way way way beyond the `255` color space! So by doing this shift, we bring the red channel to be in the first byte, which is what we want. The previous bits for the red and blue channels are now `0`:

``````001011101100010010110110 >> 16; // 000000000000000000101110
``````

## Getting the Green Channel

``````const g = (num >> 8) & 255;
``````

Well we now know why the bit shift is taking place, so it’s clear from the above that we only need to shift the values a single byte so the green channel can be the first byte, so let’s shift that:

``````001011101100010010110110 >> 8; // 000000000010111011000100
``````

But hold on, this number is still way too large! That’s because we removed the green channel, but of course the red was shifted too and is still in memory.

Introducing the bitwise `&` operator.

The `&` operator combines the bit values of 2 numbers. For each bit, it returns `1` if both bit values equal `1`, else it will be `0`.

So `3 & 2` is `2`. Because only the 2nd bit matches:

``````3: 011
2: 010
=  010
``````

So how does that help us? As I said, if either bit does not equal `1`, it is set to `0`. So, by using the expression `num & 255`, we limit the value to only ever represent 8 bits. This is because `255` is the highest number possible in a single byte and looks like this `11111111`.

``````num: 0010111011000100
255: 0000000011111111
=    0000000011000100
``````

Bye bye red channel!

## Getting the Blue Channel

``````const b = num & 255;
``````

You’ll be pleased to know that blue is the easiest to get. We don’t have to do any bit shifting as it’s the first channel. But we do need to remove the 2 other channels. So we can just use the same expression `num & 255` operator here to remove them.

``````num: 001011101100010010110110
255: 000000000000000011111111
=    000000000000000010110110
``````

## Returning

``````return [r, g, b];
``````

We now have each channel stored as seperate variables as opposed to a single value. We can now return this however we like, as an array, an object or whatever. :)