Over the last week I’ve had the opportunity to work a bit more on the skin of the blog turning my attention to the last major component of the skin not to receive some love and attention, the blog header. So far with the exception of a few icons I have managed to build the entire skin without using any images. No background images, no header images, just plain old CSS. Whilst the result wouldn’t win any creative design awards its not an ugly skin either.
I’ve wanted to add a logo for a little bit of visual tweak, a bit of branding I guess. I could have mocked something up in photoshop easily enough however creating a programmatic logo had some appeal. Luckily HTML5 affords me this opportunity with not one but two methods. Strictly speaking not HTML5, SVG could have been used and this is something I do want to look at in the future, however for the logo I decided to take a look at <canvas> which supports a basic drawing API.
The logo I had in mind was basically a mashed together A and B, a fairly simple geometric shape and one that I thought would be fairly simple to implement. The API is JavaScript and how it works reminded me a lot of an old program from my distant youth called funnily enough “Logo”. Overall I found the experience to be very easy however there are a number of concepts which are not obvious from the documentation but which I figured out eventually. I’ll take you through a step by step example shortly.
Before I do that I’d also like to hightlight the text I’ve used in the header banner. Although having been around for a very long time, downloadable web fonts have long been hard to achieve cross-browser. Legal issues have meant that the font format for @font-face has long been debated. Recently it looks like this has been resolved and WOFF has won the format war. Luckily for me and my blog Firefox 3.6 supports the WOFF format and it being my target browser of choice I decided to implement it in the banner. The font I have used is NuvoWeb which is a free font. I won’t go into the implementation as it isn’t that exciting, it is however good to be using a native font without having to resort to a work around such as image replacement or flash replacement.
Anyway back to the use of <canvas>. I’m first going to describe the basics before going through an example of creating the logo I use on this blog now. The basics I’m going to describe are obviously not an exhaustive list just what I required to make the logo.
Canvas Basics
To start using canvas you must first add one to your page this is simply a case of adding the following html:
Using the id attribute you can then grab DOM handle to it in JavaScript and start drawing, in my examples I’ve wrapped this in a function and this is called in the onload handler, barebones the handle looks like this:
var canvas = document.getElementById("test");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
}
Now we have a handle on the canvas object we can start drawing. Ther are 4 main base methods for drawing complex shapes (or paths):
- beginPath()
- closePath()
- stroke()
- fill()
Using stroke() or fill() implicitly calls closePath() also. The <canvas> is made up of pixels which are referenced as a coordinate system from the top left (0,0), using this coordinate system you have 4 methods on top of the base methods for drawing paths.
- lineTo(x, y)
- arc(x, y, radius, startAngle, endAngle, anticlockwise)
- quadraticCurveTo(cp1x, cp1y, x, y)
- bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
Finally there is also one other method which allows you to position the start point of a path or paths:
I’m going to use the lineTo() and arc() methods to create the logo
Creating the logo
Using a piece of graph paper 10×10 I sketched out the logo I desired using full squares to make the maths easier. I then translated the coordinates of each point into the functions. The following are the steps I went through, if you would like to see the code behind each logo please click on the image for a demo file.
Step 1. Creating the outside shape
The logo is made up of simple lines and a couple of half arcs making up the ‘B’.
ctx.beginPath();
ctx.moveTo(20, 180);
ctx.lineTo(60, 180);
ctx.lineTo(70, 140);
ctx.lineTo(120, 140);
ctx.lineTo(130, 180);
ctx.lineTo(200, 180);
ctx.arc(200, 140, 40, (Math.PI/180)*90, (Math.PI/180)*270, 1);
ctx.arc(200, 60, 40, (Math.PI/180)*90, (Math.PI/180)*270, 1);
ctx.lineTo(60, 20);
ctx.closePath();
ctx.stroke();
Step 2. Cutting out the inside shapes
The inside shapes are fairly simplistic as well, 3 new paths were added.
ctx.beginPath();
ctx.moveTo(80, 100);
ctx.lineTo(110, 100);
ctx.lineTo(100, 60);
ctx.lineTo(90, 60);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.moveTo(160, 50);
ctx.lineTo(160, 70);
ctx.lineTo(190, 70);
ctx.arc(190, 60, 10, (Math.PI/180)*90, (Math.PI/180)*270, 1);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.moveTo(160, 130);
ctx.lineTo(160, 150);
ctx.lineTo(190, 150);
ctx.arc(190, 140, 10, (Math.PI/180)*90, (Math.PI/180)*270, 1);
ctx.closePath();
ctx.stroke();

Step 3. Filling in the logo
Up until this point the logo has just been an outline however I wanted a solid logo. In order to do this I assumed that instead of using 4 paths, 1 path would be required, so I modified the paths to utilising moveTo() and created a single path, then used the fill() method.
ctx.beginPath();
ctx.moveTo(20, 180);
ctx.lineTo(60, 180);
ctx.lineTo(70, 140);
ctx.lineTo(120, 140);
ctx.lineTo(130, 180);
ctx.lineTo(200, 180);
ctx.arc(200, 140, 40, (Math.PI/180)*90, (Math.PI/180)*270, 1);
ctx.arc(200, 60, 40, (Math.PI/180)*90, (Math.PI/180)*270, 1);
ctx.lineTo(60, 20);
ctx.lineTo(20, 180);
ctx.moveTo(80, 100);
ctx.lineTo(110, 100);
ctx.lineTo(100, 60);
ctx.lineTo(90, 60);
ctx.lineTo(80, 100);
ctx.moveTo(160, 50);
ctx.lineTo(160, 70);
ctx.lineTo(190, 70);
ctx.arc(190, 60, 10, (Math.PI/180)*90, (Math.PI/180)*270, 1);
ctx.lineTo(160, 50);
ctx.moveTo(160, 130);
ctx.lineTo(160, 150);
ctx.lineTo(190, 150);
ctx.arc(190, 140, 10, (Math.PI/180)*90, (Math.PI/180)*270, 1);
ctx.lineTo(160, 130);
ctx.fill();
As can be seen from the resultant image the result was not what I expect. Where I had cut out shapes I expect the fill to honour them. This was the first problem. It wasn’t obvious to me from the documentation why this hadn’t worked as expected so I decided to take a different approach.

Step 4. Cutting out the inside shapes (take 2)
Instead of cutting out the inside shapes I decided to fill the shapes in with the same background colour as they were painted in, utilising a layer effect as in photoshop.
ctx.fillStyle = "#000000";
ctx.beginPath();
ctx.moveTo(20, 180);
ctx.lineTo(60, 180);
ctx.lineTo(70, 140);
ctx.lineTo(120, 140);
ctx.lineTo(130, 180);
ctx.lineTo(200, 180);
ctx.arc(200, 140, 40, (Math.PI/180)*90, (Math.PI/180)*270, 1);
ctx.arc(200, 60, 40, (Math.PI/180)*90, (Math.PI/180)*270, 1);
ctx.lineTo(60, 20);
ctx.closePath();
ctx.fill();
ctx.fillStyle = "#FFFFFF";
ctx.beginPath();
ctx.moveTo(80, 100);
ctx.lineTo(110, 100);
ctx.lineTo(100, 60);
ctx.lineTo(90, 60);
ctx.closePath();
ctx.fill();
ctx.beginPath();
ctx.moveTo(160, 50);
ctx.lineTo(160, 70);
ctx.lineTo(190, 70);
ctx.arc(190, 60, 10, (Math.PI/180)*90, (Math.PI/180)*270, 1);
ctx.closePath();
ctx.fill();
ctx.beginPath();
ctx.moveTo(160, 130);
ctx.lineTo(160, 150);
ctx.lineTo(190, 150);
ctx.arc(190, 140, 10, (Math.PI/180)*90, (Math.PI/180)*270, 1);
ctx.closePath();
ctx.fill();
This approach looks to have the desired effect.
Step 5. Adding a shadow
Currently the logo looks a little flat so I decided to add a drop shadow to the whole thing. <canvas> has a set if method for doing this so I added the relevant code:
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
ctx.shadowBlur = 2;
ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
The more eagle eyed amongst you will now spot the issue I have. Where I have layered the shapes the shadow isn’t present, it looks like my inside shape method hasn’t worked. At this point I was fairly stumped.

Step 6. Cutting out the inside shapes (take 3)
After re-reading the documentation a few times I came across the following passage:
Note: Thus, if two overlapping but otherwise independent subpaths have opposite windings, they cancel out and result in no fill. If the overlapping but otherwise independent subpaths have the same winding, that area just gets painted once.
Windings is not a term I had come across before so after some digging it looks like it is the direction (clockwise or anticlockwise) the shape is created in. All I had to do was go back to my single path and cut the bits out in the oppoiste direction.
ctx.beginPath();
ctx.moveTo(80, 100);
ctx.lineTo(90, 60);
ctx.lineTo(100, 60);
ctx.lineTo(110, 100);
ctx.moveTo(160, 50);
ctx.lineTo(190, 50);
ctx.arc(190, 60, 10, (Math.PI/180)*270, (Math.PI/180)*90, 0);
ctx.lineTo(190, 70);
ctx.lineTo(160, 70);
ctx.moveTo(160, 130);
ctx.lineTo(190, 130);
ctx.arc(190, 140, 10, (Math.PI/180)*270, (Math.PI/180)*90, 0);
ctx.lineTo(190, 150);
ctx.lineTo(160, 150);
ctx.moveTo(20, 180);
ctx.lineTo(60, 180);
ctx.lineTo(70, 140);
ctx.lineTo(120, 140);
ctx.lineTo(130, 180);
ctx.lineTo(200, 180);
ctx.arc(200, 140, 40, (Math.PI/180)*90, (Math.PI/180)*270, 1);
ctx.arc(200, 60, 40, (Math.PI/180)*90, (Math.PI/180)*270, 1);
ctx.lineTo(60, 20);
ctx.lineTo(20, 180);
ctx.fill();
This filled the shape correctly and whats more the shadows worked perfectly as well.
Step 7. Adding a gradient fill
The final touch I want to implement was a nice gradient fill so again it didn’t look too flat. You can create a different types of gradient and then apply them to the fill style so this is what I did:
var fs = ctx.createLinearGradient(0,0,0,180);
fs.addColorStop(0, '#c08e93');
fs.addColorStop(1, '#BE1E2D');
ctx.fillStyle = fs;
Logo complete!
Conclusion
Overall, with the exception of the windings aspect, I found <canvas> fairly intuitive to work with. Creating aimple shapes like this doesn’t take a lot of brain power, sure its a bit fiddly but I’m sure in time tools will be developed to make this a lot easier.
As for the logo I’m pretty happy with the result for anyone interested here is my the final complete script
function drawLogo() {
var canvas = document.getElementById("logo");
if (canvas.getContext) {
var c = canvas.getContext("2d");
var offsetX = 14;
var offsetY = 14;
var scale = 5;
var fs = c.createLinearGradient(0,0,0,18*scale);
fs.addColorStop(0, '#c08e93');
fs.addColorStop(1, '#BE1E2D');
c.shadowOffsetX = 2;
c.shadowOffsetY = 2;
c.shadowBlur = 2;
c.shadowColor = "rgba(0, 0, 0, 0.5)";
c.fillStyle = fs;
c.beginPath();
c.moveTo(offsetX+(6*scale), offsetY+(8*scale));
c.lineTo(offsetX+(7*scale), offsetY+(4*scale));
c.lineTo(offsetX+(8*scale), offsetY+(4*scale));
c.lineTo(offsetX+(9*scale), offsetY+(8*scale));
c.moveTo(offsetX+(14*scale), offsetY+(3*scale));
c.lineTo(offsetX+(17*scale), offsetY+(3*scale));
c.arc(offsetX+(17*scale), offsetY+(4*scale), (1*scale), (Math.PI/180)*270, (Math.PI/180)*90, 0);
c.lineTo(offsetX+(17*scale), offsetY+(5*scale));
c.lineTo(offsetX+(14*scale), offsetY+(5*scale));
c.moveTo(offsetX+(14*scale), offsetY+(11*scale));
c.lineTo(offsetX+(17*scale), offsetY+(11*scale));
c.arc(offsetX+(17*scale), offsetY+(12*scale), (1*scale), (Math.PI/180)*270, (Math.PI/180)*90, 0);
c.lineTo(offsetX+(17*scale), offsetY+(13*scale));
c.lineTo(offsetX+(14*scale), offsetY+(13*scale));
c.moveTo(offsetX, offsetY+(16*scale));
c.lineTo(offsetX+(4*scale), offsetY+(16*scale));
c.lineTo(offsetX+(5*scale), offsetY+(12*scale));
c.lineTo(offsetX+(10*scale), offsetY+(12*scale));
c.lineTo(offsetX+(11*scale), offsetY+(16*scale));
c.lineTo(offsetX+(18*scale), offsetY+(16*scale));
c.arc(offsetX+(18*scale), offsetY+(12*scale), (4*scale), (Math.PI/180)*90, (Math.PI/180)*270, 1);
c.arc(offsetX+(18*scale), offsetY+(4*scale), (4*scale), (Math.PI/180)*90, (Math.PI/180)*270, 1);
c.lineTo(offsetX+(4*scale), offsetY);
c.closePath();
c.fill();
}
}