HTML5 has introduced a whole new way to display multimedia content in the browser. Learn how to paint masterpieces in code.

What’s the draw of Canvas?
Keine Kommentare

Image licensed by Ingram Image.

You love developing for the web, you’ve done this for quite a while and you’ve have seen a lot of cool techniques come and go over the past decade. Haven’t you felt kind of limited one time or another when it came to developing more than simple form-based applications? Especially when it comes to developing games?

A multiplayer action RPG like BrowserQuest wouldn’t have been possible in 2002. Ever since the proof of concept by 9elements an unbelievable amount of experiments and projects have popped up on the internet. Beside games and widgets, major companies like Google now hire real time graphics experts to build interactive media campaigns based on HTML5 Canvas technologies.

Already hooked by these projects? Then it’s time dive in!

A retrospective

In the past, companies tried to impose proprietary multimedia solutions, most frequently in the form of a plugin, as a standard by making use of their market dominance. It was a highly lucrative strategy, as developers and software companies had to licence their tools in order to create content for these plugins. In order to visualise graphics and interactive content in a performance oriented fashion, Flash based technologies were most common. Frequent implementations included pixel perfect websites, video-players, animations, simple games, rich internet applications and various types of visualisations. Despite the fact that it had to be installed on the computer, Flash spread widely and reached a huge acceptance level among users.

Post-Web 2.0 developments

This dominance started to crumble some years ago. JavaScript performance has improved and HTML5 finally offered developers open and standardised ways to handle multimedia content and real-time animations much better without the need for additional plugins. In short, it’s now easier that ever before.

A worthwhile and unique improvement is HTML5 canvas. At first glance, this new DOM-Element seems to have very limited functionality. Similar to a chessboard, the canvas is made up of indexed fields and has fixed dimensions. Every field of the board has a unique coordinate, to which a colour value is assigned. The coordinate system is, as opposed to the usual conventions in math, upside down.

In the past years, developers have stressed that designers must get used to the idea that websites are meant to be flexible. Not pixel perfect! Ideally, a website is based on responsive methodology and is using responsive CSS techniques. Is this all obsolete now? Will everyone be developing their websites by just using Canvas from now on?

Actually, exactly the opposite is the case. The trend is still running towards responsiveness. Canvas should be seen as an additional tool in the developer’s box that enables us to realize certain challenges. In the past, it was impossible to achieve some goals without using browser add-ons – especially when it came to projects which made use of graphical animations or even realtime manipulations.

When to use canvas

With applications increasingly moving from the desktop to the browser, Google has led the way with Chrome OS and its focus on web apps. For applications alike office or mail, there is nearly no real need for well performing graphics components like canvas: You can just stick to most of the known ways of developing web applications. But imagine the following scenario: How would, for instance, a photo editing software be implemented in the browser? A smart web developer will definitely come up with some solutions based on using back-end technology. In reality though, processing every pixel of a picture over an internet connection feels rather awkward.

Another typical case in which you can make a good use of canvas are diagrams. While bar charts can be implemented with conventional HTML-based solutions, this is not the case with curve diagrams or even pi charts, especially if updates in realtime are required. In this case, canvas provides a reasonable advantage by being able to draw non-rectangular shapes and the capability to manipulate the diagram on the client side. In the past, it was common to render the diagrams on the server, download and display them in the browser. This resulted in server side computational load and growth of data traffic.

First Steps

When seen isolated and as separate entity, canvas seems less than spectacular. It appears more like an empty image container. By generating an instance with , the browser only creates a new area displayed as transparent surface:


There is a special behaviour of canvas, as, similar to the element, is also based on pixels. When you define a element with the given dimensional attributes, you will also set up the pixel buffers with the given height and width values. By default, the actual size of the element (as displayed within the browser document) will also have this given size. By using CSS to manipulate the width/height of the canvas, you will just stretch or shrink the projection within the DOM. The real size of the pixelbuffer will stick to the initial values.

If you would like to change or manipulate the contents of the canvas, you have to make proper use of JavaScript. Each instance of has a context of its own.


The first parameter of the method getContext in the code above defines what kind of context is to be received from the . In this case, the parameter has the value “2d”, which results in receiving the context for the 2D drawing API. Beside the 2D API, many modern browsers support an additional type of context for drawing in a three dimensional manner. The technology underneath the 3D drawing is called WebGL, a substandard of OpenGL ES.

By this, it is becoming more obvious that canvas itself is just simple “projection plane”. It is the contexts which contain all the logic for drawing into the pixel buffer. The buffer itself is a one-dimensional array. Its size is four times as large as the total amount of managed pixels. When a canvas is defined with 640 pixels in width and 480 pixels in height, the size of the pixel buffer is 1228800 fields (640 * 480 * 4 = 1228800). This is because of the internal data structure. For a single pixel there is a red, blue, green and alpha value defined. This fact also enables you to manipulate the image data directly, like you may be used to back in the days of writing games or intros on the Amiga.

var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
// manipulation of
context.putImageData(imageData, 0, 0);

Canvas uses the data from the pixel buffer and creates a graphical representation of it. This also relates to the fact that canvas images are stored in a different size than they are displayed within the document. A very important difference between the canvas 2D API and WebGL is that canvas is using software rendering (CPU driven) only. WebGL performs much better because it can make use of the graphics hardware of a device (GPU driven). So what’s the disadvantage? The trade-off is with the complexity of the API. Pure WebGL, without using any framework or library, comes with a lot more programming overhead. The reason for this is that with WebGL you are always working very close to the graphics hardware, even when using a high-level language like JavaScript. You will also have to have a very good knowledge of 3D math as well. Compared to this, canvas 2D comes with a very simplified 2D API. It is best for realizing two dimensional applications.

Besides the possibility of manipulating the raw pixels, the 2D context also supports a huge variety of methods for executing primitive drawing operations. Combined with paths, curves and external loaded images, it should be possible to display nearly any imaginable object on a canvas. For commons shapes like rectangles, circles or text, there are convenient methods defined by default. All that the context must be told before the drawings are carried out is which color stroke and fill it shall be done with. This information can be given as a HTML/CSS compatible hex or even as rgba value. Yes, RGBA indeed. As mentioned earlier, canvas internally handles an RGBA model. So canvas can also handle transparency regions on a variety of depths.

The code below shows an example function that draws 100 lines with a randomly-generated color. See it in action here.

// Draws 100 lines with a randomly generated color
function draw() {

    // get the #a-canvas from the DOM, 
    // request a drawing context
    // generate a random color split by r, g and b 
var canvas  = document.getElementById('a-canvas'),
    context = canvas.getContext('2d'),
    randomX = canvas.width * Math.random(),
    randomY = canvas.height * Math.random(),
    r       = Math.round(Math.random() * 255),
    g       = Math.round(Math.random() * 255),
    b       = Math.round(Math.random() * 255),
// paint the canvas in a solid black
// by drawing a hughe black rectangle 
// onto the canvas
context.fillStyle   = '#000000';
context.strokeStyle = 'rgb(' + r + ', ' + g + ', ' + b + ')';
context.fillRect(0, 0, canvas.width, canvas.height);
context.moveTo(randomX, randomY);

// draw a 100 lines, pointing towards
// randomly generated locations within
// the canvas’ dimensions.
for(i = 0; i < 100; i++) {
    randomX = canvas.width * Math.random();
    randomY = canvas.height * Math.random();
    context.lineTo(randomX, randomY);

// submit the drawing by calling stroke
// and closing the 'path' of lines

Canvas uses an imperative drawing model. This means that the context is drawing only when it is told to and it includes a step by step command model. This is the opposite to WebGL, where you have to set up all model data and send them to the pipeline. Changes on the 2D API are executed and stored in the pixel buffers immediately when context.fill() or context.stroke() have been called. It is comparable to the final command in a submission process. This is because the basic drawing functions like lineTo(x, y) don’t draw to the pixel buffer directly, but collect coordinates until stroke or fill is called. Methods like fillRect(x, y, width, height) already describe in their method signature in which way a shape should be drawn. There is no need call fill separately: The drawing will be executed directly.

Canvas neither creates nor remembers any objects by itself, as you might be used to with other graphical APIs or scene graphs. Drawing works in a “fire and forget” manner. The moment when the interpreter leaves, for instance, fillRect, any information about the past drawing is gone – except for the projection drawn to the buffer, of course.

But the context isn’t stateless! Besides the mentioned stroke and fill style configurations, there is some kind of configuration stack, though you cannot manipulate it directly. By calling one can save the context’s current configuration. When calling and then modifying (for example) context.fillStyle immediately after, you can reset the context to the state before modification by just calling context.restore(). This can be done as often as you please:

context.fillStyle = '#ff0000';;

context.fillStyle = '#00ff00';;

context.fillStyle = '#0000ff';;
alert(context.fillStyle); // '#0000ff'

alert(context.fillStyle); // '#0000ff'

alert(context.fillStyle); // '#00ff00'

alert(context.fillStyle); // '#ff0000'

There is nothing like a direct stack limitation. Indeed, this mechanism is working like a stack. You can roll back only one configuration at a time and you won’t be able to re-access a rolled-back configuration later. Just like the plain old Last In First Out principle.

This configuration stack applies for the context attributes only. It does not apply to the pixel buffer. The buffer stays the same instance at any time.

But why is this feature really useful? Actually, nearly every single time you update the attributes of the context and you need to restore the original state later. Think of methods having particular needs for manipulating color or stroke styles, but at the same time don’t want to mess up its caller’s configurations:

function drawSomething(context) {;

    // (...)
    // configure and draw
    // (...)


By applying this strategy, it is ensured that the caller of drawSomething(context) can depend on his or her own changes to the context, even when drawSomething(context) has been executed. This only works when all callees are aware of this approach and stick to it.


It might seem like a little bit of overhead to choose this approach when it is just about remembering color values. But as mentioned before, save/restore applies to the whole configuration stack. This includes transformations too.

As we know, there is no scene graph with canvas. Therefore, manipulations are stored as context configuration. If you want to draw a rectangle that is rotated 45 degrees, you have to tell the context to do so before context.fillRect has been called:

var PI_DIV_180 = Math.PI / 180,
    degree     = 45,
    radians    = degree * PI_DIV_180;;
context.translate(100, 100);
context.fillStyle = '#ff00ff';
context.fillRect(0, 0, 100, 100);

Transformations are a very strong tool when it comes to manipulating shapes in a 2D space. The best part: there is no need for heavy knowledge of trigonometrical functions! Translations, rotations or scales are simply applied over the canvas context. Context.translate(x, y) simply moves the origin for all upcoming drawing operations, given by x and y. Context.scale() handles separate scaling factors for x and y axis, where 1.0 represents the original scale. If Context.scale() is just given the first parameter, the scale will be applied symmetrically for the y-axis. A scale of 0.5 is equivalent to reducing the to be drawn object in half of its original size. A scale of 2.0 says that the next drawing will be twice as large as the dimensions of the context.

There are a few things to know when it comes to context.rotate(radians): in analogy to the trigonometric functions of JavaScript’s Math (and as in nearly every other programming language as well), is also context.rotate(r) defined to work with radians instead of degrees. Usually we handle rotations in everyday situations using degree units, and so 45, 90, 180, 360 degrees is much easier to imagine for the most people than 0.25π, 0.5π, π, 2π. This is a little pitfall over here, that beginners regularly walk into and should be aware of.However, it is in fact very easy to convert degrees into radians. Without digging too deep into the mathematical background: 1 degree is equivalent to (Math.PI / 180) radians. Therefore you are still able to stick with degree units in you application, as long as you multiply the degree value with (Math.PI / 180) every time before you pass it into a trignometric function or rotate(). Using degrees comes also very handy, because in many cases of animations or effects you tend to adjust your computation parameters in a proportional manner (eg. degree++). Incrementing the degree value by a natural type of number feels way better than a multiple element of (Math.PI / 180).


As a next step, you should understand how animations work in the first place. The human eye is relatively slow and lazy. Even at a very low frame rate, the transition of each frame cannot be noticed and is rather perceived as constant movement. The flip book is based on making use of this effect. The same principle applies to computer animation. Canvas is implemented in a way that allows rapid repainting of the projection. As a rule of thumb, a refresh rate between 35 Frames per Second (FPS) and 60 FPS is perfect for most animations.

Similar to many newly introduced CSS attributes, the requestAnimationFrame is prefixed in most browsers and not supported by older browsers:

function draw(timestamp) {
    var totalTime = timestamp - startTime;
    }, 1000 / 5); 

var startTime =;

// Alternatives:
//   webkitRequestAnimationFrame(draw);
//   mozRequestAnimationFrame(draw);
//   oRequestAnimationFrame(draw);
//   msRequestAnimationFrame(draw);

Since JavaScript is an almost fully flexible language, it didn't take the web community long to develop a workaround.


The higher the frame rate, the better the effect, flow and realism of the animation. One should take into account that every frame has to be calculated and drawn by the software each time. Canvas 2D, being a software-rendering API, easily reaches its computational limit, resulting in shaky, stuttering and slow animations. Many performance related problems can easily be resolved with a few adjustments.

In order to work with Canvas one should notice the following: Canvas coordinates are integers or integral numbers accordingly formulated! Despite the fact that the Drawing API accepts floating point numbers as valid entries, to make use of them should be avoided at any cost. Remembering that the internal data structure of canvas is to be seen as a grid, there can’t be any room between the fields. Actually, canvas handles floating point coordinates very gracefully using interpolation. For example, if the X-coordinate in any drawing operation is defined as 5.5, it will begin drawing an antialiased version of the projection. This has the effect that drawn objects seem to be placed at a fractional coordinate.

This behaviour results from the fact that the drawing API operates with a pixel buffer, which is an array. Arrays do not recognise float indices, so the browser interpolates with the (for instance) 2D API. The interpolation and antialiasation processes will slow the performance down. Therefore always try to give non-floating numbers to a 2D context method. A positive side-effect is that the edges of the drawn elements stay sharp.

Otherwise, this usually leads to a noticeable performance drop. Based on recent benchmarking, Chrome seems to be least affected, while Firefox performs significantly worse.

Call it a blessing or curse: many animations and games that are using the Canvas 2D API come close to the technological level of applications from the mid-nineties. Among other things, this enables us to learn from the experience accumulated from the graphic programming of the nineties – days in which demo coders or game developers wrote their animations without any additional 3D hardware, just software rendering.

For example, bitshifting was a common method for improving the performance of certain computations. Although Douglas Crockford (in "JavaScript: The Good Parts") recommends to avoid bitshifting due to performance reasons, bitwise operations seem to achieve good benchmarking results in most current browsers.

With Platforms on which RAM is not an issue, a trade off between memory and performance can be made. When ensured that specific values (coordinates, colours, factors) never outrun a particular range, it can be an option to create a lookup-table for these datasets – colour pallets as used in plasma-effects or similar animations, for instance. Nearly every result of calculations that rely on trigonometrical functions are suitable for this technique.


Canvas is a great thing to keep just more than one eye on! Basically it is time to rethink some web methods we have adopted over time. A lot of solutions that have been put together by developers over the years are just workarounds, born out of technological handicaps.

Everyone familiar working with graphics using object hierarchies, similar to DisplayObjects of ActionScript/Flash, should look out for libraries which provide these features. Hardware-accelerated, three-dimensional rendering is on the rise thanks to WebGL and libraries like ThreeJS.

When it comes to mobile devices, the implementation of is very unfortunate. If you are out for more than drawing some minor, infrequently-updated graphics, you won’t get on very happily with . For smoothly-displayed animations, you are better off with CSS animations. In particular, the WebKit browsers have some huge advantages when it comes to the quality of CSS3 implementation on mobile devices. This includes Animations, Transitions as well as Transformations or using the few 3D features of CSS3.

Canvas has already heavily influenced the development of plugin free multimedia websites. Even major companies like Disney and McDonalds are open to having some of their campaigns implemented with use of canvas. The next years are very promising!

Dennis Wilson is a freelance software developer and consultant for web-, mobile-, and interactive-applications. His focus is mainly on interactive computer-graphics and client based technologies.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Benachrichtige mich bei
Inline Feedbacks
View all comments
- Gib Deinen Standort ein -
- or -