DEV Community

Cover image for Utilising Bitwise Operators in JavaScript, by Building a RGB to Hex Colour Convertor
Owen Rees
Owen Rees

Posted on

Utilising Bitwise Operators in JavaScript, by Building a RGB to Hex Colour Convertor

A Real World Example of Bitwise AND, OR, and Left/Right Shift

Bitwise operators are a seldomly used part of the JavaScript language, being much more popular in low-level languages such as C. After my previous article on solving a popular coding challenge using the Bitwise XOR operator, we are now going to look at a real world example of using some other bitwise operators, by creating our own RGB to Hex and Hex to RGB colour convertors. These colour code convertors can also be created without using bitwise operators, but that wouldn't be as fun!

The Lineup

In this article, we will be using the following bitwise operators:

Please pay attention to the & and | symbols, as they look similar to their logical counterparts && and ||.

In this article we can afford a bit more time to talk about how each of these operators functions.

Bitwise AND (&)

Bitwise AND is truthy when the two bits being compared are 1, otherwise falsy.

x y x AND y
0 0 0
0 1 0
1 0 0
1 1 1

Comparing two binary representations of numbers, each bit is compared against the other, and the resulting bit is set to 1 or 0 depending on the evaluation.

const result = 10 & 5 // 2

// compare each column:
1010    // 10
0110    // 5
----
0010    // 2
Enter fullscreen mode Exit fullscreen mode

Bitwise OR (|)

Bitwise OR is truthy when when either or both bits are 1.

x y x OR y
0 0 0
0 1 1
1 0 1
1 1 1
const result = 10 | 5 // 13

// compare each column
1010    // 10
0110    // 5
----
1110    // 13
Enter fullscreen mode Exit fullscreen mode

Bitwise Left Shift

This operator shifts the bits to the left by the specified amount, while shifting in zero bits from the right. We will use an equal amount of bits for the comparisions, for easier visualisation.

// we can see that the singular 1 has been moved to the left by 4 places
const result = 8 << 4 // 128
0000 1000 << 4 = 1000 0000 

// the bits have all been shifted to the left 3 places
const result = 55 << 3 = 440
0000 0011 0111 << 3 = 0001 1011 1000
Enter fullscreen mode Exit fullscreen mode

Bitwise Right Shift

This operator shifts the bits to the right by the specified amount, where excess digits are pushed off the right.

const result = 256 >> 4 // 32
0001 0000 0000 = 0000 0010 0000

// here we see excess bits being pushed off the edge on the right
const result = 440 >> 6 // 6
0001 1011 1000 = 0000 0000 0110
Enter fullscreen mode Exit fullscreen mode

RGB to Hex

Let's take the Hex colour code #157f4c. First we must understand what the Hex colour code represents. Rather than being a large hexdecimal number (0x157f4c = 1408844), we must split this into groups of 2, each representing the value of R, G, and B.

Hexadecimal Decimal Binary
0x15 21 00010101
0x7f 127 01111111
0x4c 76 01001100

Our RGB value is 21, 127, 76

Our goal is to make one long 24-bit binary representation of our RGB values, as such:

21 127 76
00010101 01111111 01001100

00010101 01111111 01001100

To achieve this, we need to move the bits for the R and G values to the left, by 16 and 8 bits respectively. For this we will use the Bitwise Left Shift (<<) operator.

// Binary representations are in 24-bit space

// R value
00000000 00000000 00010101 << 16 = 00010101 00000000 00000000

// G value
00000000 00000000 01111111 << 8 = 00000000 01111111 00000000

// B value
00000000 00000000 01001100
Enter fullscreen mode Exit fullscreen mode

We have three binary numbers, and we need to create one long 24-bit binary number with them. We can achieve this using the Bitwise OR (|) operator, as follows:

// R | G, compare each column using the rules for the bitwise OR operator
00010101 00000000 00000000 |
00000000 01111111 00000000
--------------------------
00010101 01111111 00000000

// and now this new value | B
00010101 01111111 00000000 |
00000000 00000000 01001100
--------------------------
00010101 01111111 01001100  // the desired binary representation of our RGB values
Enter fullscreen mode Exit fullscreen mode

Pay attention to how the bitwise OR is working in the above example, concatenating the binary values into one long value.

Let us now write a function to tie this all together.

function rgbToHex(rgb) {
    // shifting the bits to the left and comparing with bitwise OR
    const binaryRGB = rgb[0] << 16 | rgb[1] << 8 | rgb[2]   
    // convert the binaryRGB string to a hexadecimal (base-16) string
    const hex = binaryRGB.toString(16)      
    // add '#' to the beginning. Make sure the returned value is always 6 characters long, padding with a 0 if not.
    return `#${hex.padStart(6, '0')}` 
}

console.log(rgbToHex([21, 127, 76]))  // values taken from a form etc. 
// '#157f4c'
Enter fullscreen mode Exit fullscreen mode

Hex to RGB

And now to do the inverse, taking a Hex colour value and returning RGB values. Let's use the same Hex code that the previous section returned, to prove that it is correct by returning our original RGB values.

First of all, we need to work with a real hexadecimal value:

const hex = '#15014c'.replace('#', '0x') // '0x15014c'
Enter fullscreen mode Exit fullscreen mode

If we view this hexadecimal in its binary representation again, we get:
00010101 01111111 01001100

R G B
00010101 01111111 01001100
21 127 76

Now we need to store each of the R, G, and B values in their own variables. In order to create the correct binary number for each of these values, we must now bitwise right shift the R and G values by 16 and 8 bits respectively.

00010101 01111111 01001100 >> 16 = 00000000 00000000 00010101 // R (21)
00010101 01111111 01001100 >> 8 = 00000000 00010101 01111111  // G (4319)
00010101 01111111 01001100 // G (219948)
Enter fullscreen mode Exit fullscreen mode

Wait, these values are incorrect! We need to take one more very important step, which would leave us with just the required bits.

Comparing the binary representation with Bitwise AND (&) against 11111111 will leave us with just the last 8 bits of information.

// Compare the columns

// R
00000000 00000000 00010101 &
00000000 00000000 11111111
--------------------------
00000000 00000000 00010101  // (21)

// G
00000000 00010101 01111111 &
00000000 00000000 11111111
--------------------------
00000000 00000000 01111111  // (127)

// B
00010101 01111111 01001100 &
00000000 00000000 11111111
--------------------------
00000000 00000000 01001100  // (76)
Enter fullscreen mode Exit fullscreen mode

Pay attention to how the bitwise AND operator works in the above examples, and how it leaves us with only the required information.

Let us now tie this all together in a function.

function hexToRgb(hexColour) {
    // replace '#' with '0x' to give us a real hexadecimal string to work with
    const hex = hexColour.replace('#', '0x')  

    // right shift the respective bits
    // followed bit masking with bitwise AND against '11111111' (0xff in hexadecimal)
    const r = hex >> 16 & 0xff
    const g = hex >> 8 & 0xff
    const b = hex & 0xff

    return [r, g, b] // return an array of the RGB values
}

console.log(hexToRgb('#15014c'))
// [ 21, 1, 76 ]
Enter fullscreen mode Exit fullscreen mode

Conclusion

In this article we saw a real world example of using bitwise operators in JavaScript, by building a RGB to Hex and Hex to RGB colour convertors. If you have never dealt with bitwise operators before, there is a lot to take in here. However there is no better way to learn a concept than by actually building something!

If you are looking for further reading on the topic, try the following articles:

JavaScript Bitwise Operators

MDN - Expressions and Operators

Using Bitwise operators in JavaScript

Top comments (0)