Here’s a short preview of what we’re going to get in to. First, we create a HTML5 canvas and use that to create a gradient image using some predefined colors. After that’s done, we create another image using the same template and some tweaked behavior. Next, we implement fading between the two images. At this point the icon is essentially done. For polish, we add feathered edges with customizable sharpness and size.
This tutorial is fairly long, so here’s a link to each of the sections:
The first thing you’re going to want to do is get a testing environment set up. I would definitely recommend using Google Chrome or Mozilla Firefox to build this, as they are the most modern and supported browsers right now. Next, if you already have a webpage or a local webserver set up, great! Just create a new blank html page and include a script in the body, like so:
If you don’t have an environment already set up, I recommend using a fun website called JSFiddle to test your changes. There’s no HTML or page setup needed to test! The drawback is that you can’t directly modify the .favicon on that website. Don’t worry, we can simply display the canvas in addition to writing it to the icon.
Let’s start out by displaying a basic canvas. The first thing we need to do is actually create the canvas element. Most people have a canvas element just hanging around in their HTML waiting to be used. Since we’re doing this procedurally, we might as well make the HTML procedural too!
We also need to answer an important question. How big is our canvas going to be? Favicons are all square sized, (16x16, 32x32, etc). While they used to be pretty small (around 16px), on new devices they can be over 200 pixels! Since there are a lot of possibilities, I’ll leave it up to you to decide how big you want to make it. I’m going to choose 32 pixels.
Let’s add a canvas to the document. And while we’re at it, we might as well add a icon element too:
Sure, we have a canvas now, but we can’t see anything! There are simple ways to fill a canvas, whether it’s through an image source or a fillRect call. But for our purposes, we need pixel-level image manipulation. Let’s run through the canvas and fill all the pixels with red.
To do that, we need to create a context for the canvas. We’re going to be using the 2D context - the HTML5 canvas element also supports 3D rendering through WebGL. Once we have the context, we’re going to create some empty image data. This is what will allow us to manipulate the pixels directly.
Now we’re ready to iterate through the pixels. Each pixel is made up of 4 components: r, g, b, a. These stand for red, green, blue, and alpha (the transparency of the pixel). Each component is one element of the data array that the context created for us. That means that we need to loop over every 4th array member.
After we’re done assigning red to the pixel, we need to apply the new pixel’s color to the canvas, using the context. To do this we need the pixel’s location on the canvas. Rather than do a two dimensional for loop, I prefer to derive the location using a little math.
Imagine the pixel data set up in this 4x4 grid.
If you were to label these by array element, it would look like this:
Now, what is the result when you integer divide each value by the size (4)?
Yep, it’s the y value of the pixel data! And now, for the modulus division (%) by the size (4):
You guessed it, this represents the x value of the pixel data.
The last step of our derivation is to account for the fact that we’re looping 4 times for every pixel. Since we’re incrementing at 4 times the number of pixels, we can divide the incrementer by 4 to get the correct pixel. Then we can finally update the canvas with the new pixel data. Let’s update our for loop:
Still no image! Don’t worry, there’s only one more step before we see results. Remember that icon element we made at the beginning of this section? We need to give that an updated link with the new canvas info. Put this after the for loop:
If you’re using JSFiddle, you also need to add the canvas to the document before you can see it. You can do this at the top of the script after you create the canvas.
Behold! A red square! Pretty sweet, huh? To recap, we just:
Assigned the canvas pixel data for each pixel
Linked the icon to the canvas to display it
And here’s the finished code for this section:
We have a working canvas. Let’s generate our images! The plan for this program is to interpolate between two images. I’m a big fan of gradients, so each image is going to be a linear gradient that stretches diagonally across the canvas. The gradient will be defined by two colors. Let’s define those two colors for the first image now. I’m going to pick two of my own colors, but you can pick whatever colors you want. Just make sure the alpha value is 255 so you can see it. Put this code above the for loop:
To do the interpolation, we’re going to need to find the parameter t that defines how much to interpolate between the two colors. Basically, if t is 0, we’ll be displaying color 1, and if t is 1, we’ll be displaying color 2. We’ll be calling this parameter lerpValue for readability. For our first image, (0, 0) will correlate with a lerpValue of 0, and (iconSize - 1, iconSize - 1) will correlate with a lerpValue of 1. This means that as x and y grow, lerpValue will increase relative to the icon size. Make sure to divide by two to account for the equal contribution of x and y. Let’s make iconSize - 1 its own variable called maxIndex, and within our code, make the following changes:
Now that we have the t parameter for the lerp function, we need to apply it to the pixels. It’s as simple as lerping between the respective components of color 1 and color 2 using the lerpValue. We’re going to need to rearrange some things in the for loop, as well:
You should get this awesome gradient as your favicon (or canvas image) now.
For the second image I want to shake things up and make the gradient go from the bottom left of the image to the top right of the image. To do this, we’re going to need to change lerpValue. Since the rest of the code for the second image will be virtually the same except for that one variable, let’s try changing the direction of our current gradient.
This time around, a lerpValue of 0 will correlate with (0, maxIndex), while a lerpValue of 1 will correlate with (maxIndex, 0). This makes it a bit harder to find the relationship between the two. For the x component, things have stayed the same: as x increases, lerpValue increases. But for the y component, things have inverted. The y value starts at maxIndex and decreases. Let’s split up our calculation into two parts:
Using the power of math, we can combine the two into our final statement:
Now you can see how we rotated the gradient! We now have all the image-making tools we need to move on the the next step: making it dynamically fade between two images! To recap, we:
Created color objects that represent our gradient.
Interpolated between those colors when drawing to the canvas to create the gradient.
Rotated the gradient by changing the interpolation parameter.
Here’s the finished code for this section:
Making It Dynamic
Making the images is great and all, but making it all dynamic is so much better! We’re going to create 2 different gradients using the method that we currently have, save them into data arrays, then fade between them over time.
We need to create two arrays that will hold the data for our images. We can’t just assign them to the value of ctxImageData, since that will link the data by reference and will overwrite the other images if we try to manipulate the data. The data is stored in the Uint8ClampedArray type. Put this after ctxImageData is declared:
One more thing, we need to define the colors for our second image! Again, you can choose any colors you want, but these are the ones I’m going to choose:
Now, let’s create the images as we have before, but store them in our new data arrays this time.
Note that for the first image, we use the top left to bottom right gradient direction, and for the second image, we use the bottom left to top right gradient direction.
We still have the loop over the context’s image data. This should no longer be using image 1’s data, but it should actually be interpolating between image 1 and image 2. Let’s update the loop to reflect this:
If you look at the imageLerpValue for this loop, you will notice that I just set it to 0. We’re going to change this later, but for now you can preview what the fade is going to look like if you set imageLerpValue to values between 0 and 1. For example, this is what image 2 looks like (imageLerpValue set to 1):
What we want to do is change imageLerpValue over time to get a smooth fade between the images. That means that we need to call that loop repeatedly. Let’s make a variable that determines how often we call that for loop. Let’s also place the for loop in its own function. That way, we can easily call it from setInterval(), which calls a function repeatedly after a set amount of time. Make sure to also put the icon update in this function.
The updateRate is in milliseconds, so the update() function is being called every half second.
To update the image over time, we need some method of tracking how long the program has been running for. If we can get a number that increases over time, we can pass that into a sin function and get a nice smooth fade like so:
To track the time, we need a time variable which tracks the accumulated time, and a lastTime variable which holds the last (date) time calculated. We’ll also add a fadeMultiplier that will allow us to change how fast the fade between the two images is. Then we can calculate the delta time, or the change in time, since the last time the function was called and add it to the accumulator.
To get our smooth fade, we can put the time variable in the sin() function. sin() returns a number between -1 and 1, and our interpolation wants the range 0, 1, so we can do a little math to change the range of the sin function:
We can pull imageLerpValue out of the for loop (but still within update) so we don’t have to keep recalculating it during the image drawing.
Now we should have a (slowly) fading icon! Remember, you can change the fadeMultiplier variable (along with updateRate if it starts to look choppy) to change how fast the icon fades.
One more thing before I wrap up this section: you may notice the canvas is blank for a short amount of time when the page loads. This happens because setInterval() waits before it calls the function for the first time. So before the setInterval call in your code, add an initial update() call:
Alright, so we have a fully dynamic, procedural .favicon now! The icon is pretty much done at this point. The last thing that’s left is some polish! To recap, we:
Created data arrays for our two images and changed the canvas loop to interpolate between them.
Called the canvas drawing at a set interval.
Kept an accumulator of time passed and used a sin wave to determine the interpolation between the images.
Here’s the code up through this section:
Adding Feathered Edges and Custom Size
Now on to the final step, polish! When I was making the icon originally, I wasn’t happy with the sharp square shape. I wanted something more circular. So, I decided to use some math to modify the transparency of the pixels as they were being generated.
To generate a circular shape, pixels that are far away from the center need to become more transparent. We need a formula to track the distance from the center. Since we don’t care about the actual distance, but only a relative distance to the center, we’ll use the distance squared formula initially:
We need to have a variable that represents the maximum distance a pixel can be from the center. That would be at (0, 0) or (maxIndex, maxIndex), which is a difference of (maxIndex / 2, maxIndex / 2) in both cases. Thus, our maximum distance is:
Put this above the update function.
Next we need to calculate the distance each pixel is from the center during the canvas drawing. We know the center point will be (maxIndex / 2, maxIndex / 2), so the distance is just the absolute value of the x and y from maxIndex / 2. Make sure this goes in the update() function under the currentX and currentY variables and above the pixel manipulation:
We need to take these distances and map them into a single value that represents the distance from the center. 1 would be the most transparent and at the center, which 0 would be at the corners and have no transparency. We’ll take the distance formula we looked at before and divide it by the maximum distance. However, this will give us a value of 1 for the furthest away pixels, so we’ll make sure to invert the result:
To see these changes, change the alpha pixel to use the distance ratio multiplied by 255 - this will correctly scale the alpha color. Here’s the full for loop to make sure we’re on the same page:
It’s a good start, but not where we want it yet. We can see that the pixels do get more transparent as they get closer to the corners, but we need more fine-tuned control over the fading to get our desired result.
We’re going to define two settings to modify our transparency fade: falloffPower and falloffSize. The falloffPower determines how sharply the transparency fades. The falloffSize determines the modifies the maxDistance, causing the fade to end at a closer or farther distance.
We’re going to modify maxDistance now. First, we’re going to multiply the maxDistance by the falloffSize to have the effect of making the icon smaller or larger. Then, we’re going to going to take maxDistance to the power of falloffPower, which may sound counterintuitive but as long as we apply this to distRatio as well, we’ll maintain the correct relationship between the two. maxDistance now looks like this:
And now let’s add the pow to distRatio as well:
Nice! We have our shape where we want it, but it still looks a bit blurry. Let’s clamp the alpha ratio to 1 unless it falls below 0.5.
That looks good to me. That’s our finished icon!
To recap, we:
Faded the pixels during canvas drawing using a maximum distance.
Added falloff power to control the sharpness of the icon edge.
Added falloff size to control the size of the icon.
Here’s the final code for the icon:
I hope you liked the tutorial! May your .favicon’s be ever changing!