Home Articles FAQs XREF Games Software Instant Books BBS About FOLDOC RFCs Feedback Sitemap
irt.Org

Related items

How to do the impossible with Google Gears

Out and About

JavaScript Bouncing Balls

Multi-dialogue forms on one page

Turning Tables Into Selection Lists

Drag and Drop with Microsoft Internet Explorer 5

Dynamic Floating Tool Tips

What is DHTML?

Image Manipulation Techniques

"The Light Fantastic"

String Gradients the Fun Way!

You are here: irt.org | Articles | Dynamic HTML (DHTML) | String Gradients the Fun Way! [ previous next ]

Published on: Sunday 16th May 1999 By: Ryan Detert

Have you ever been to a site where words or sentences are composed of what appear to be two blended colors? Ever wondered how on earth to do it without using a fancy program or wracking your brain trying to figure out which letter should be what color to generate a nice blend in straight HTML coding? Well in this tutorial we are going to play around with some of the more advanced features that JavaScript offers, as well as some advanced techniques in order to get a nice shading effect.

In case you don't already know, your monitor displays everything via pixels, which are tiny dots on the screen that are composed of a red, green, and blue part. In fact, these colors are the also the primary colors of our color spectrum. ALL colors that we see are composed of a mixture of wavelengths of red, green, and blue light. In order to color in a pixel, we are able to adjust the intensities of the red, green, and blue parts of the pixel to form different colors. For example, if all of these parts are turned off the pixel is black, if all of them are on the pixel is white, and if only the blue part of the pixel is turned on the pixel will be blue.

The hexadecimal numbering system is a base 16 number system, unfortunately, we all use a base 10 number system. What does this mean you ask? Well, all that it means is that you have six extra digits to deal with. In EVERY number system that you will ever see, they ALL work the same way, except you write them using digits from either a wider or narrower variety. In the base 10 numbering system, decimal, we use the digits from 0-9 to write all our numbers. In base 16, hexadecimal, we use the digits of 0-F. Where A=10 decimal, B=11 decimal, C=12 decimal, D=13 decimal, E=14 decimal, and F=15 decimal. How do I count in this strange number system? Why the same way that you count in decimal of course except you have to use ALL the digits.

So instead of counting like this in decimal: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20...

We will instead count in hexadecimal in this manner: 0 1 2 3 4 5 6 7 8 9 A B C D E F 10 11 12 13 14...

Reading hexadecimal numbers is done the same way that you read in decimal, although you may not realize it. In the decimal system that we use, everything is a power of 10, even though we don't really think of it in this way.

For example, the number 16345 in decimal is really:

1 x 104 + 6 x 103 + 3 x 102 + 4 x 101 + 5 x 100 = 16345

So, in hexadecimal, this number would be 3FD9 (and remember that F=15 decimal and D=13 decimal), which may also be expressed as:

3 x 163 + F x 162 + D x 161 + 9 x 160 = 3FD9

Amazing! So now all of you have a general idea of the hexadecimal numbering system and you all realize that there is no difference really between our decimal system and the hexadecimal numbering system or any other number base system. Computer gurus have decided on making hexadecimal the standard for computer languages and such even though the computer itself operates in a base 2 system, which we'll discuss later. If you have ever done any lower level programming you will realize that hexadecimal is easier to read because it also allows us to write huge decimal numbers using fewer digits. I will not go into how to convert between bases because it is beyond the scope of this tutorial and all that you really need is a calculator for converting and an understanding of what hexadecimal is.

Well in most programming languages, even HTML, there is what is known as an RGB triplet, which is how we assign color values to the red, green, and blue components of a pixel. An RGB triplet, for our purposes, will be 3 bytes, which is 1 byte for each component of the RGB triplet. Notice that since there are 8 bits in a byte, an RGB triplet is also 24-bit color. The range of a single byte is from between 0-255 in decimal, which is equivalent to 0-FF in hexadecimal. In HTML you may have seen something like this, <font color="#FF0000">, well the color value is actually an RGB triplet that is being read in hexadecimal by the computer. Notice that it is 6 digits long, an RGB triplet CANNOT be more than six digits long because it can only contain 3 bytes. In this HTML RGB triplet, '#FF12AC', the first two digits, 'FF', represent the intensity of the red component of the pixel, which is at full strength because a byte cannot be greater than FF. The next two digits, '12', represent the intensity of the green component of the pixel, it is fairly low. The last two digits, 'AC', represent the intensity of the blue part of the pixel, it is fairly high. The end result is a pixel being displayed that is a kind of pink.

Please note however that in most programming language, and JavaScript, hexadecimal number MUST be preceeded by an '0x'. This is so the computer will not confuse decimal with hexadecimal. So to write the previous RGB triplet using JavaScript, we would go 0xFF12AC. As an example of how to use RGB triplets in JavaScript we can make the background color of a page pink by going:

document.bgColor = 0xFF12AC;

By using an RGB triplet, we are able to use 256 shades of both red, green, and blue to make our colors, this works out to be somewhere around 16 million colors!

Now that we have a general idea of how pixels and color work, let's begin to talk about how on earth we are going to write a string that has a gradient like the title of this tutorial. One common remedy for this is known as GOURAD SHADING. This is a fairly simple technique that is used often in 2-Dimensional graphics programming. All that you have to do is assign two variables a starting color and an ending color and then find out how many step increments it will take to change the first color into the second. It can be represented like this:

(secondcolor - firstcolor) / number_of_steps;

The variable 'secondcolor' is the end color and the variable 'firstcolor' is the starting color. The variable 'number_of_steps' will be the length of our string in this case and we divide by this variable in order to determine how much to increment the pixel intensities per string character. Since we are dealing with a string, this line of code will more amply suit our needs.

(secondcolor - firstcolor) / string.length;

Although Gourad Shading is a fairly simple concept, we need to take one MAJOR thing into account. We MUST transition EACH of the red, green, and blue pixel components individually. If we were to merely subtract red(0xFF0000) and blue(0x0000FF) and then divide by some number, the color transition steps would not work correctly according to the physics of our color spectrum and we would end up with some pretty crumby results. Thus, we have to separate each of the red, green, and blue components of our RGB triplet and then increment each component by the same amount individually.

You may be wondering how on earth you are going to do this! Well it involves a more advanced technique known as bit-masking. Bit-masking is the art of taking away all of the bits that we don't want or don't need.

All information stored in the computer is in a binary (base 2) format, which means that the only digits that you are allowed to use are either 1 or 0. (To an electrical engineer, a 1 may mean a high charge and a 0 a low charge but just know that the computer operates in binary) So the number 20 in decimal, 0x14 in hexadecimal, is 10100 in binary. Like hexadecimal, we also count the same in binary as we do in hexadecimal or decimal, so counting from 0 - 10 would be like so:

0, 1, 10, 11, 100, 101, 110, 111, 1000, 1001, 1010

Also notice that ten in binary, 1010 is also:

1 x 23 + 0 x 22 + 1 x 21 + 0 x 20 = 10

JavaScript, C, and just about all other real programming languages allow you to get rid of bits (binary numbers) that you don't want.

There are three basic bit-mask operators. They are the AND(&), OR( | ), and XOR(^) operators. Here is a table of how they work

AND

OR

XOR

1 & 1 = 1 1 | 1 = 1 1 ^ 1 = 0
1 & 0 = 0 1 | 0 = 1 1 ^ 0 = 1
0 & 0 = 0 0 | 0 = 0 0 ^ 0 = 0

So if I want to get rid of the first three 1's in the binary number 11101011 you would go:

11101011
AND 00001011
00001011

Conversely, if I wanted to attach the three 1's to the front of the binary number 00001011 you would go:

00001011
OR 11100000
11101011

Now wait a dog-gone minute here, we can't use straight binary in JavaScript. Well guess what, we don't need to! Since all number systems work in exactly the same way, we can translate the binary numbers to hexadecimal or decimal on a sheet of paper and then get the same results using regular decimal or hexadecimal. So in decimal the first example would look like 235 & 11 = 11 and 0xEB & 0xB = 0xB in hexadecimal. Pretty cool huh!

Before we can fully separate the components of the RGB triplet, we need to cover one more important topic, bit-shifting. All that this does is literally shift the bit right or left. In JavaScript, there is a right-shift operator(>>) and left-shift operator (<<). Note that these operators work with 32-bit numbers and they fill in any gaps that may result with zeros. Here are some examples using bit-shifting in binary:

As it looks in binary:

Shift Operation

Result

11001 << 2 1100100
101011 << 4 1010110000
11001 >> 2 00110
101011 >> 4 000010
As it looks in hexadecimal:

Shift Operation

Result

0x19 << 2 0x64
0x2B << 4 0xFFFFFFE60
0x19 >> 2 0x06
0x2B >> 4 0x02

Note that a single left shift is a quicker way of multiplying by 2 while a single right shift is a quicker way of dividing by 2 also.

Throughout this section, we will use 0xAABBCC as our RGB triplet. First, we need to get the red portion of the triplet. Since a full strength red pixel is 0xFF0000, we need to get rid the 'BBCC' in our triplet. We do not have to deal with bit masking to get the red component, all that we need to do is shift out the 'BBCC'. This can be accomplished by using a right-shift operator. However, like in the rest of the tutorial, there is a trick that is easy to miss. We cannot simply shift 0xFF0000 four places to the right like below because it doesn't work mathematically.

0xFF0000 >> 4 = 0x0FF000

This is because the shift operators shift individual bits, and there are eight bits in a byte, so the above example is only shifting 0xFF0000 half a byte, and we want to shift it two whole bytes, or 16 bits. So the correct way to separate the red component in our RGB triplet is by going:

0xAABBCC >> 16 = 0xAA;

To find the green component of the triplet, we need to incorporate both or knowledge of bit-masking as well as bit-shifting. In our triplet, we want only the middle two digits, now how on earth are we going to do this? Well, since we know that anything AND'ed with 0 equals 0, we can begin by doing this:

0xAABBCC & 0x00FF00 = 0x00BB00

However, this number is too large so we must shift it right 1 byte by going:

(0xAABBCC & 0x00FF00) >> 8 = 0xBB

To get the blue component of the RGB, all that we need to do is cancel out the first two bytes, which can be done by going:

0xAABBCC & 0x0000FF = 0xCC;

After separating the RGB triplet into its three components, we must then calculate how much to increment EACH part of the RGB. It will be done as follows:

RedStep = (endRed - startRed) / string.length
GreenStep = (endGreen - startGreen) / string.length
BlueStep = (endBlue - startBlue) / string.length

Finally, we begin to discuss the actual string that we would like to transform into a gradient. In order to do this, our general strategy will be to plot each letter one by one and increment the startcolor RGB components accordingly. We will use the HTML method of displaying characters via JavaScript. Thus our code will look like this:

for (var index = 0; index < string.length;index++)
    document.write("<font color=' ",color, " '>", string.charAt(index), "</font>");

Now in order for this to work, we must convert our color variable to hexadecimal every time and we must also add a pound sign (#) in front of it as HTML syntax dictates. Note that the RGB triplet MUST be in hexadecimal form to function properly.

Before we begin to convert the RGB triplet to hexadecimal, since we alread tore apart the triplet, we have to put it back together temporarily. This is an easy task that can be accomplished with the bit-mask OR operator and some simple left bit-shifting to get all of the bits to fall into place:

var merged = (red << 16) | (green << 8) | blue;

I will touch up on this lightly. Basically, to convert from base 16 to base 10 we are just going to divided the original number by 16, save the remainder, and then continue to divide its remainder until it can be divided by 16 no more. This function will return a string instead of a number because JavaScript will convert the number back to decimal, which we would be pointless. Don't worry about understanding the code, the important thing is that you understand how hexadecimal relates to the RGB triplet.

var h="0123456789ABCDEF";

function hex(c) {
    var temp = "";
    var hexStr = "";
    var remainder, i;

    // divide c by 16 each time
    for( ; c != 0; c >>= 4) {
        remainder = c % 16;
        hexStr += h.charAt(remainder);
    }

    // only thing with a conversion is that it does it backwards
    // so this makes it right
    // you could have also used recursion but that
    // would have been more complicated
    
    for(i=5 ; i >= 0; i--)
        temp += hexStr.charAt(i);

    // return the converted hex number
    return temp;
}

Okay, now that we have gone through all of this work, there is a minute detail that we must not overlook. It is such a small detail that even I had trouble catching it. An RGB triplet in HTML must be 6 digits in hexadecimal. If it is not, the browser will append the necessary amount of zeros needed to make the triplet 6 digits. Notice that in hexadecimal, 0x001234 is the same as 0x1234, but when dealing with RGB triplets in HTML, the browser will transform #1234 into #123400 instead of #001234, as one may think. What is the reasoning behind this you might ask? Well, let's say that we start at the color black, which is 0x000000 and increment each component by 0x09 which yields 0x090909. However, our hexadecimal conversion function will return the number with only 5 digits like so 0x90909, so when we try to place this number inside the color attribute of the font tags we will end up with this result:

<font color="#909090">string.charAt(index)</font>

This isn't what we want, instead, we want the trailing zero to be placed at the beginning of the triplet. Fortunately this problem is easily solved by attaching a pound sign and the appropriate number of preceeding zeros to the number that our hexadecimal conversion function returns like so:

var FullRGB = "#000000";
color = hex(merge(red, green, blue));
color = FullRGB.substring(0, 6 - color.length + 1) + color;

<SCRIPT LANGUAGE="JavaScript"><!--
var h="0123456789ABCDEF";
var Fullrgb = "#000000";

function gradient(string, startcolor, endcolor) {
    var len = string.length;
    var color;

    var start_red = startcolor >> 16;
    var start_green = (startcolor & 0x00FF00) >> 8;
    var start_blue = startcolor & 0x0000FF;

    var end_red = endcolor >> 16;
    var end_green= (endcolor & 0x00FF00) >> 8;
    var end_blue = endcolor & 0x0000FF;

    var incRed = Math.floor((end_red - start_red) / len);
    var incGreen = Math.floor((end_green - start_green) / len);
    var incBlue = Math.floor((end_blue - start_blue) / len);


    for (var x=0; x < len; x++) {
        if (start_red + incRed >= 0x00 && start_red + incRed <= 0xFF)
	    start_red += incRed;
        if (start_green + incGreen >= 0x00 && start_green + incGreen <= 0xFF)
	    start_green += incGreen;
        if (start_blue + incBlue >= 0x00 && start_blue + incBlue <= 0xFF)
	    start_blue += incBlue;

        color = hex(merge(start_red, start_green, start_blue));

        color = Fullrgb.substring(0, 6 - color.length + 1) + color;

        document.write("<font color = ", color, ">", string.charAt(x), "</font>");
    }
}

function merge(r, g, b) {
    return (r << 16 | g << 8 | b);
}

function hex(c) {
    var temp = "";
    var hexStr = "";
    var remainder, i;

    for ( ; c != 0; c >>= 4) {
        remainder = c % 16;
        hexStr += h.charAt(remainder);
    }

    for (i=5 ; i >= 0; i--)
        temp += hexStr.charAt(i);

    return temp;
}
//--></SCRIPT>

So to use this function to blend a string from black to blue just go:

gradient("I am happy!", 0x000000, 0x0000FF);

Blending strings is by no means a simple task, but by using your knowledge of bit-masking, bit shifting, the hexadecimal number system, and the gourad shading technique it is possible. First, you must select a starting color to blend and then select an ending color to blend into. Then we must separate each part of the RGB triplet into its red, green, and blue components, and then calculate the change in the starting color components and the ending color components over a finite length we will call them (delta)red, (delta)green, and (delta)blue. We then increment each component of the starting color by its (delta)counterpart while displaying each character individually as we go along. So have fun messing around with the color schemes and amazing your friends.

Related items

How to do the impossible with Google Gears

Out and About

JavaScript Bouncing Balls

Multi-dialogue forms on one page

Turning Tables Into Selection Lists

Drag and Drop with Microsoft Internet Explorer 5

Dynamic Floating Tool Tips

What is DHTML?

Image Manipulation Techniques

"The Light Fantastic"

Feedback on 'String Gradients the Fun Way!'

©2018 Martin Webb