---
layout: post
title: Integrating Canvas into your Web App
date: 2011-08-05
updated: 2013-10-29
authors:
  - davidtong
tags:
  - blog
---

## Introduction

In this article, I am going to discuss how to use the HTML5  [canvas](https://developer.mozilla.org/docs/HTML/Canvas) element to create, edit, open and export images. I will also introduce several open-source tools that are relevant to this technology, and provide some tips on how these techniques can be applied to an existing web application. 

## Check for canvas support

The first thing to do is check that your browser fully supports HTML5 canvas. An easy way to do that is to use [Modernizr](http://www.modernizr.com) to check for a certain feature:

```js
if (Modernizr.canvas) {
  // Browser supports native HTML5 canvas.
} else {
  // Fallback to another solution, such as Flash, static image, download link, and so on.
}
```

## Creating a canvas element and importing an image as binary or data URI

First, you will need to have a canvas element in your page. 
Using JavaScript, you do the following: 

```js
var ctx = document.getElementById('new_canvas').getContext('2d');
var img = new Image();
img.src = "html5.gif"
img.onload = function () {
   ctx.drawImage(img,0,0);
}
```

In this code, the first step is to get the 2D context, which gives us access to the [API](http://dev.w3.org/html5/2dcontext/) that defines all the drawing methods and properties. Next, we create an image object and set the src property to the location of the binary image. When the image is loaded, we then use the drawImage() method to import the image into the canvas element. 
You can also use a data URI instead of the URL of an image. So instead of the URL above, you can do the following: 

```js
img.src="data:image/gif;base64, "
```



You may ask, "Why would we use data URI instead of the binary image?" There are many [advantages](http://en.wikipedia.org/wiki/Data_URI_scheme#Advantages). And later in this article, you will see how easily we can export a canvas image as data URI. 
Here is [a tool to convert a binary image file to a data URI](http://boazsender.github.com/datauri/).

## Manipulating the canvas image

If you have ever done any sort of [Logo programming](http://en.wikipedia.org/wiki/Logo_(programming_language)), drawing on a canvas uses the same concept. 
Mark Pilgrim has a [chapter on canvas](http://diveintohtml5.info/canvas.html) in his book, Dive Into HTML5. Based on an example in the chapter, we can add a grid diagram to the image that we imported above by using the following:. 

```js
var img2 = new Image();
img2.onload = function () {
  var context2 = document.getElementById('new_canvas2').getContext('2d');
  /* vertical lines then horizontal ones */
  for (var x = 0.5; x < 800; x += 10) { context2.moveTo(x, 0); context2.lineTo(x, 500); } 
  for (var y = 0.5; y < 500; y += 10) { context2.moveTo(0, y); context2.lineTo(800, y); }
  context2.strokeStyle = "#bbb";
  context2.stroke();
  context2.drawImage(img2,0,0);
}
img2.src = "html5.gif";
```

You can be more creative than this, but I leave it to the other tutorials listed in the appendix of this article for additional instructions on that subject. 
We haven't seen anything very exciting yet, but the next section will change that.

## Exporting the canvas image as data URI

The canvas element has a `toDataURL()` method, which takes a MIME type as the parameter. With this, we can export the canvas we used above. 

```js
window.open(document.getElementById('ctx').toDataURL("image/png"));
```

This exports the canvas as a PNG image into a new browser window. The image, however, is not an ordinary binary image, but is instead a base64-encoded data URI that can be rendered by a browser. Thus, from the user's standpoint, there is no difference between that and the binary equivalent. 
Note that the line of code above needs to be run on a web server. Running `toDataURL()` on a local file would fail. See [this ticket](http://code.google.com/p/chromium/issues/detail?id=21847) for the status of this issue in Chrome. 

## Integrating into your web app
Canvas can be a very powerful add-on to any web applications that store user-uploaded images. 

<figure>
{% Img src="image/T4FyVKpzu4WKF1kBNvXepbi08t52/Xm0Z35ZmkZRdxNswrrAP.png", alt="Box canvas", width="460", height="155" %}
</figure>

For example, we have an online file storage application that stores user-uploaded images. We can add an edit button to open the image file in a canvas-based picture editor. 
If you don't want to write your own canvas editor, [Harmony](http://mrdoob.com/projects/harmony/) is one of the few canvas editors openly [available](https://github.com/mrdoob/harmony). It features easy addition of brushes, which can satisfy your artistic tastes. 
When you choose "edit image" in the menu illustrated above, a canvas editor should open, and it would make a call to a custom `read_file()` function in the editor's init() function as follows: 

```js
function read_file() {
   var url = file_id;
   // hide a copy of the original image if it is needed to load
   document.getElementById('editableImage').src = url; 
   image = new Image();
   image.src = url;
   image.onload = function() {
      context.drawImage(image,0,0); // context, defined above, as canvas.getContext('2d')
   }
}
```

<figure>
{% Img src="image/T4FyVKpzu4WKF1kBNvXepbi08t52/hWOLKp6PgSNkOXkxt0XT.png", alt="Harmony", width="448", height="323" %}
</figure>

## Adding HTML5 LocalStorage

A little touchup that you should always consider--if you care about user experience--is applying LocalStorage. For example, if you have a big text area that requires the user to input a lot of information. When the user is about to submit the form, he accidentally closes the browser (or the browser crashes). The user might be frustrated and not bother rewriting the message again.
In the demo below, instead of saving the data onto the server, simply save the image onto LocalStorage as data URI:

```js
// Save Image
function saveToLocalStorage() {
    localStorage.setItem('canvas', canvas.toDataURL('image/png'));
}

// Load Image
function init() {
        // for demo purpose, all variables are declared in the parent scope
        canvas = document.createElement('canvas');
        context = canvas.getContext('2d');

        // Use Modernizr to detect whether localstorage is supported by the browser
        if (Modernizr.localstorage && localStorage.getItem('canvas'))
        {
            localStorageImage = new Image();
            localStorageImage.addEventListener("load", function (event) {
                //...
                context.drawImage(localStorageImage, 0, 0);
            }, false);
            localStorageImage.src = localStorage.getItem('canvas');
        }
//...
}
```


## Saving canvas as a binary file onto the server
You may want to save the canvas image as a binary file. There are many ways to do that. For example, you can perform a POST action to pass the data URI to your backend code. Using jQuery, it would look like this: 

```js
var url = '/api/write/' + file_id + '?data_url_to_binary=1';
var data_url = flattenCanvas.toDataURL('image/png');
var params = { contents: data_url };

$j.post(url, params, function(json){
   if (json.status == 'upload_ok')
   {
      //ok
   }
}, 'json');
```

This creates a XHR call with the content being the data URI. 
You then need to decode the base64 data URI on the server. In [PHP](http://ch2.php.net/base64_decode), for example, you can do the following: 

```js
if ($_GET['data_url_to_binary'])
{
   $contents_split = explode(',', $contents);
   $encoded = $contents_split[count($contents_split)-1];
   $decoded = "";
   for ($i=0; $i < ceil(strlen($encoded)/256); $i++) {
      $decoded = $decoded . base64_decode(substr($encoded,$i*256,256)); 
   }
   $contents = $decoded; // output
}
```

In the first two lines, the data URI ($contents) is split into two parts. `'data:image/png;base64'`, and `'VBORw0KGgoAAAANSUhEUgAAAWwAAAB+CAIAAACPlLzKAAAACXBIWXMAAC4jAAAuIwF4pT92...'` We will then use `base64_decode()` to decode the data URI string. The trick here is that there are issues decoding string greater than 5K, and this "divide-and-conquer" approach will be able to decode the string. 
Finally, using [fwrite()](http://php.net/manual/en/function.fwrite.php), you can save the binary file, $contents, onto your server. 

## Enabling "save image" in browser

Canvas is a HTML element. It looks pretty much like an image, but your browser does not provide a "Save Image As" option for it because it is not really an image element. 
To enable "Save Image As," you may dynamically create an Img element, and set the src to the data URI of the canvas element. 
You can also use the [canvas2image utility](http://www.nihilogic.dk/labs/canvas2image/).

## A more advanced canvas editor

If you are looking for a more advanced canvas editor, [PaintWeb](http://code.google.com/p/paintweb/) is probably worth a try. It was written by Mihai Sucan, a Romanian student, during the Google Summer of Code 2009. He also authored a few [tutorials](http://dev.opera.com/articles/view/html5-canvas-painting/) on writing your own online paint application. 

<figure>
{% Img src="image/T4FyVKpzu4WKF1kBNvXepbi08t52/d8JqwEwFx7QMi8Bg1lZe.png", alt="Paint Web", width="771", height="706" %}
</figure>

For a more professional library, be sure to check out [Pixati](http://www.pixastic.com/lib/).

## More fun with canvas? 
Paul Irish combined Harmony and $1 Unistroke [Recognizer](http://depts.washington.edu/aimgroup/proj/dollar) to create a little [Easter Egg](http://paulirish.com/2010/my-harmonious-background-canvas/) on his website. 

You can also learn how to [inspect canvas](http://www.html5rocks.com/en/tutorials/canvas/inspection/) with the Chrome DevTools using our recent inspection features.

## Go deeper with additional tutorials on canvas

- [MDN: Canvas Tutorial](https://developer.mozilla.org/docs/Web/Guide/HTML/Canvas_tutorial?redirectlocale=en-US&redirectslug=Canvas_tutorial)
- [Dive into HTML5: Canvas](http://diveintohtml5.info/canvas.html)
- [HTML5 canvas - the basics](http://dev.opera.com/articles/view/html-5-canvas-the-basics/) on Dev.Opera covers the basic drawing primitives
- [Building a Breakout clone in canvas](http://billmill.org/static/canvastutorial/ball.html) includes basic motion, physics and interactivity