## A tutorial by jim bumgardner (@jbum on twitter)
This is a tutorial on how to produce sequences of discrete colors, for use in fonts, graphics or tables, such as in the color strips shown here, or the title of this article.
Obviously you can make a webpage look incredibly annoying if you
overuse this technique (and by
I am going to use javacsript for my code examples, but you can use the same basic techniques in any language, and indeed I originally developed these tricks in C, but I now use the same old tricks in C++, Perl, Processing, Java, Flash Actionscript and other languages.
To get the most out of this tutorial, you will need to know a little Javascript, and a little HTML. However, you won't
need to know a whole lot. I mean c'mon. I even explain how
Well, let's get to it, shall we?
## Converting R,G,B values to HTML hex notationOn computer monitors, you get different colors by combining red, green and blue. The red, green and blue levels are called 'color components'.To generate rainbow-like sequences of colors, I am going to be manipulating the individual RGB components of each subsequent color. So the first thing I need is a utility function to convert individual red, green and blue values to an HTML hex color specification, which looks something like this: #AABBCC This represents an RGB color with 8-bits per component. In an 8-bit color system, the values of the color components, R, G, and B can go from 0-255. But the HTML color specification uses hexadecimal notation for each of the components, so that they each occupy two digits. In hexadecimal, each color component (red, green, and blue) occupies two digits, like so: #RRGGBB. So our color has the following hex values:
If you don't have a calculator handy that does hexadecimal -> decimal conversion, it's not too hard to do it yourself. Just take the first digit, convert it to decimal (using the above table, which is easily memorized, especially if you're not busy friday), multiply it by 16, and then add the decimal equivalent of the second digit. For example, the number AB corresponds to 10*16 + 11, or 171. Using this formula, you will see that the color #AABBCC corresponds to the following values:
What I need for this tutorial is a function that does the reverse - converts 170,187,204 back to #AABBCC. I would use it like this:
output = RGB2Color(170,187,204); And know that the string '#AABBCC' is in the output value, ready to be output as HTML, using the document.write() command. Here is the function. function RGB2Color(r,g,b) { return '#' + byte2Hex(r) + byte2Hex(g) + byte2Hex(b); }My conversion function relies on yet another utility function, byteToHex(), which converts each of the color components from a numeric value (such as 170) to a hexadecimal string (such as 'AA'): function byte2Hex(n) { var nybHexString = "0123456789ABCDEF"; return String(nybHexString.substr((n >> 4) & 0x0F,1)) + nybHexString.substr(n & 0x0F,1); }This function relies on the binary arithmetic operators >> and &, which treat the numbers as binary, which is closely related to hexdecimal. Every 4 bits (or binary digits) in a binary number corresponds to a single hex digit, 0-F. So this function takes the leftmost 4 bits of the number (given by n >> 4, which shifts the number right by 4 bits) and converts it to hex, and then takes the rightmost 4 bits of the number (given by n & 0x0F, which masks off the rightmost 4 bits) and converts it to hex. I should point out that decimal -> hex conversion (and number formatting in general) is much easier in languages that provide a 'printf' function - something sorely missing from Javascript. In Perl, you can do this same conversion in one line, like so: output = sprintf '#%02x%02x%02x', $r, $g, $b; If you can live without an old-style CSS color specification, you can also produce colors more simply without byte2Hex, like so: function RGB2Color(r,g,b) { return 'rgb(' + Math.round(r) + ',' + Math.round(g) + ',' + Math.round(b) + ')'; }
## Sine wave cyclesThe basic tool I am going to use to make color cycles is the sine wave. There is a function in Javascript, Math.sin() which will produce a smoothly undulating value that goes from 0 to 1 to 0 to -1 and back to 0. Here are some samples:
Sine waves are To get this pattern, I have input into the Math.sin() function a value which is steadily increasing. You can produce a series of sine wave values using a loop, like so: var frequency = .3; for (var i = 0; i < 32; ++i) { Document.write( Math.sin(frequency * i) ); }You may have noticed that the sine wave pattern starts to repeat right about when frequency*i is equal to 6.2. The precise value where it repeats is actually 2π (2 times PI) or 6.28318, which happens to correspond to the circumfrence of a circle with a radius of 1. As you may already know, sine waves are closely related to circles. The halfway point in the sine wave is π exactly, or 3.14159... At this time I would like to apologize for how crappy the π (PI) character looks in the san-serif font used in 80% of the computers out there. If I were using lovely Times Roman, the π character would be far more recognizeable. Sadly, I'm not, because for just about everything else I find the San Serif font more readable. But I digress...
There is a relationship between these π units which are used with the Math.sin() function (called
You can produce a value for π in Javascript by saying: document.write(Math.PI);Like so:
I Speaking of gray matter...
## Using sine waves to make shades of grayI can use the return value of Math.sin() to make fluctuating color values. However, I need to do a little conversion. As shown above, the return value of Math.sin() goes from -1 to 1. The color components I use to make RGB colors go from 0 to 255. So I need to translate a number from the range (-1 --> 1) into (0 --> 255).
This isn't too hard because we know that we want the sine value to start at some center value, and then
go up and down by some amount. Since the sine wave goes up to 1, and down to -1, it is already For our color values, it will work well if we use 255/2 for our center value, and have the sine value go up and down with an amplitude of 255/2 (which will take us to 255 and down to 0.
color_component = Math.sin(frequency*i)*255/2 + 255/2;I usually express this, using integers, as: color_component = Math.sin(frequency*i)*128 + 127;...which isn't exactly the same, but close enough. More generally, when we want to use a sine wave to oscillate something, we will use the formula value = Math.sin(frequency*increment)*amplitude + center;
Now I can modify my original sine wave loop, and produce a series of test colors. var frequency = .3; var amplitude = 127; var center = 128; for (var i = 0; i < 32; ++i) { v = Math.sin(frequency*i) * amplitude + center; // Note that █ is a unicode character that makes a solid block document.write( '<font style="color:' + RGB2Color(v,v,v) + '">█</font>'); }Inside the document.write I am producing a color by calling RGB2Color(v,v,v). Since I pass the same value in 3 times, for red, green, and blue, I will get a gray color. In gray colors, red, green, and blue have the same value. To view the color, I print out a block character, using the unicode value █. And here's what the code produces -- a kind of color sine wave:
For fun, I can try using a series of different values for frequency. This is what I'll get if I add an additional outer loop that changes the value of frequency.
## Using out-of-phase sine waves to make rainbowsNow, as I mentioned, the reason the above gradient looks gray is beacuse I am using the same value for red, green, and blue. One way we can try to get colors is to add a constant to the value of v that we are using for the green and blue parameters. So instead of saying:RGB2Color(v,v,v)I might say: RGB2Color(v,v+30,v+60)This produces the following color sequence:
This is interesting, but it is not quite what I want. A better technique is to generate 3 out-of-phase sine waves, by doing something like this: var frequency = .3; for (var i = 0; i < 32; ++i) { red = Math.sin(frequency*i + 0) * 127 + 128; green = Math.sin(frequency*i + 2) * 127 + 128; blue = Math.sin(frequency*i + 4) * 127 + 128; document.write( '<font color="' + RGB2Color(red,green,blue) + '">█</font>'); } Which produces this:
As you can see I am using the values 2 and 4 to change the alignment (or phase) of the green and blue sine waves. I chose 2 and 4 because they nearly divide the range of the sine wave (2π or 6.2) into three equal parts, which puts each sine wave approximately 1/3rd of a cycle, or 120°, out of phase, like so: red (phase = 0) green (phase = 2) blue (phase = 4) RGB
If I wanted
However, the results are so close, that I think 2 and 4 are fine substitutes. It is said that ancient Egyptians thought that π was equal to three.
And in this tutorial, we're gonna
## More about the hue cycle
This basic phenomenon - that three 120° out-of-phase sine waves produces a rainbow effect is something I've known
about for a long time - since the mid 1980s actually. I originally discovered it just by playing around with using sine waves to make color (a frequent activity which took the place of Later, when I learned more about color theory, I found out that what I'm doing is basically causing the hue of the color to travel around the circle of a color wheel. If you plot the path that I'm making on the hue-saturation circle commonly seen in color pickers, you'll find it doesn't produce a perfect circle, but more of a trefoil pattern, like so:
Note: I used the Processing language to produce this illustration. Making it in Javascript would be a bit of a chore. If you like graphics programming in Javascript, you'll love it in Processing.js, and you will enjoy my Processing blog (and unfinished book): The Joy of Processing. Even though it doesn't make a perfect hue circle, the sine-wave trefoil makes a pretty color gradient, and it's pretty easy to get a rainbow effect in just a few lines of code. I actually prefer it to the "correct" HSB / HSL hue cycle, because it has more consistent brightness. It minimizes the spokes at yellow, cyan and magenta, which you can see in the picture. Now, I'll talk about how to modify the function to produce a greater assortment of colors, and different shades, such as pastels.
## Generalizing itIn the next few sections, I'm going to use a general purpose function for drawing these color gradients. It allows me to specify separate parameters for frequency and phase for each color component. Here's the code:function makeColorGradient(frequency1, frequency2, frequency3, phase1, phase2, phase3, center, width, len) { if (center == undefined) center = 128; if (width == undefined) width = 127; if (len == undefined) len = 50; for (var i = 0; i < len; ++i) { var red = Math.sin(frequency1*i + phase1) * width + center; var grn = Math.sin(frequency2*i + phase2) * width + center; var blu = Math.sin(frequency3*i + phase3) * width + center; document.write( '<font color="' + RGB2Color(red,grn,blu) + '">█</font>'); } }And here's the code for drawing the basic rainbow gradient. makeColorGradient(.3,.3,.3,0,2,4);Which looks like this:
## Making pastelsThe basic trick to getting pastels is to change the gamut of the color components to use lighter colored values for the color components. So, when we convert the sine values, instead of convering to the full range (0 --> 255), we convert to something like (205 - 255). We change the last two parameters (center and width) as follows. Before: // center = 128, width = 127 makeColorGradient(.3,.3,.3,0,2,4, 128,127);after: // center = 230, width = 25 makeColorGradient(.3,.3,.3,0,2,4, 230,25);In the changed code, the 230 is the center of the sine wave, and the 25 is the maximum deviation from this center value. So the sine wave starts at 230, goes up to (230+25) and goes down to (230-25). In other words, it has a range of (205-255). And this makes a pastel strip:
I can get darker pastels by using a wider range (center=200, width=55), like so:
## Get more stripes by increasing the frequencyWhen you change the value of frequency, you can get the colors to change more quickly, or more slowly. Up to now, I've mostly been using an frequency value of .3, but here are some other values:
## Experimenting with phaseFor the basic rainbow effect, I separated the phases of each sine wave by 120°. What happens if I put the phases closer together? Let's find out:
## Get more variety by using separate frequencies for each componentAnother trick I've used which produces greater color variety is to put each color component on a different frequency. Here's the code: redFrequency = .1; grnFrequency = .2; bluFrequency = .3; center = 128; width = 127; makeColorGradient(redFrequency,grnFrequency,bluFrequency,0,2,4,center,width,50);...and here's what it looks like:
If I separate out the individual channels, you can see what is going on: red (freq = .1) green (freq = .2) blue (freq = .3) RGB Using three different frequencies may produce colors with a low saturation value, such as the black seen in the example above, or grays or whites. This happens when the three sine waves cross the same value simultaneously.
## Getting cycles to repeatLet's say you want the color cycle to repeat every 6 steps. How do you do it? The way I accomplish this is by using a frequency value which corresponds to 1/6th of 2π. Remember that the sine wave repeats every 2π, so this will make the colors repeat every 6 increments. Here's the code...center = 128; width = 127; steps = 6; frequency = 2*Math.PI/steps; makeColorGradient(frequency,frequency,frequency,0,2,4,center,width,50);And here's what it looks like:
## Getting cycles to not repeatIf you don't want the colors to repeat exactly, then use frequency values which don't go evenly into 2π. It turns out that the number 2.4 works very well for this. 2.4 in radians, is very close to the golden angle (137.51°), which is the angle that many plants grow new shoots to maximize the sunlight received by leaves. Here I'm using essentially the same technique - I am maximizing the distance between color repeats (which would be like overlapping leaves around a stem). 2.4 is a good frequency to use if you're selecting colors for a pie chart or graph, and you're not sure how many data values you're going to need to plot. Here's the code.center = 128; width = 127; frequency = 2.4; makeColorGradient(frequency,frequency,frequency,0,2,4,center,width,50);And here's what it looks like with a frequency of 2.4:
If you combine this with the trick of using separate freqencies for each color, and insure that none of the individual frequencies are multiples of each other, you can get even more variety. Here I'm using frequency of 1.666, 2.666, and 4.666. center = 128; width = 127; redFrequency = 1.666; grnFrequency = 2.666; bluFrequency = 3.666; makeColorGradient(redFrequency,grnFrequency,bluFrequency,0,0,0,center,width,50);
Finally, here's a function that does the rainbow effect on a line of text. function colorText(str,phase) { if (phase == undefined) phase = 0; center = 128; width = 127; frequency = Math.PI*2/str.length; for (var i = 0; i < str.length; ++i) { red = Math.sin(frequency*i+2+phase) * width + center; green = Math.sin(frequency*i+0+phase) * width + center; blue = Math.sin(frequency*i+4+phase) * width + center; document.write( '<font style="color:' + RGB2Color(red,green,blue) + '">' + str.substr(i,1) + '</font>'); } }
Well, that's it. I hope you enjoyed the tutorial!
If you have any questions or comments, add them to the blog entry about this article, which you'll find here. My second Javascript tutorial: Using the Arcane Operators in Javascript/Actionscript My other blog: The Joy of Processing
Privacy Policy Contact Krazydad Discord server |