How to Save an HTML5 Canvas as an Image With .toDataURL()
Tags: html, webdev, javascript, canvas
Published: 2020-06-29 14:18:51

Let’s say you’ve created a web application that uses an HTML canvas to dynamically render something, generated automatically or based on user input. Great! You’ve done a lovely thing and brought some razzle-dazzle to the cold glow of your user’s browser window.

Now, let’s say you’d like that user (if logged in) to be able to save that canvas to their profile for display later on, to save it to your application’s database anonymously, or to save it to their computer as an image file. This is exactly where the *[toDataURL()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL)* method comes into play.

*.toDataURL()* is a method built into the modern HTML5/JS browser ecosystem and meant to be called on a canvas element somewhere on a web page. It has two parameters:
<br/>

> canvas.toDataURL(**type**, **encoderOptions**)

* **type** indicates the file format that you’d like the canvas to be saved as, by default a PNG.
* **encoderOptions** is a number between 0 and 1, indicating the image quality for lossy file formats like JPEG or WEBP. The default value is 0.92, used if no value is entered.

The remarkable feature about the *.toDataURL()* method is that it doesn’t export a canvas directly as an image file as we’re most familiar with them, but rather as a [Data URL](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs), as the name suggests.

A Data URL, in the context of an image, is essentially the binary data of an image file encoded in [Base64](https://en.wikipedia.org/wiki/Base64), to be displayed as an ASCII string.

That’s right, this means that you can represent an image like this:
<br/>

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/4aiqr759fbsc6eeg40ni.jpg)
<br/>

as an ASCII string like this:
<br/>

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/0py1t28f4rpa5m233yfx.png)
<br/>

Pretty wild, right?

This might be common knowledge to those well versed in the history of binary data being transferred across the internet, but it’s a surprise when you see it for the first time. This is also how image files are sent over email as attachments, and how images are often transferred and stored in databases worldwide.

Precisely what we’re trying to do with our application!

***

For an example in context, we’ll use an application that a fellow developer friend and I worked on in our bootcamp earlier this year. We developed an application called [The Fractal Zone](https://thefractal.zone/), an interactive site that allows users to input different rule sets and values and generate fractal designs in real time on a canvas. Crunchy stuff!
<br/>

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/sgs8xiy8r11ty5uvilf2.png)
<br/>

We knew we wanted these canvas designs to be persisted in our Rails back-end database, but needed to figure out a way to do it. We were using PostgreSQL, and we knew we couldn’t directly save image files in the database. Once again, here comes *.toDataURL()* to the rescue.

In our database schema, we set a parameter for a saved fractal as the type text (an extra-large string), with the name *image*:
<br/>

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ovr7ysqqeohc9oco4rvn.png)
<br/>

This sets us up to save our canvas as an image by encoding it as a string on the front end. We’re doing so in a method that’s called on a Save button-click event, like so:
<br/>

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/nevzcrv16li7u9nmiszj.png)
<br/>

And that’s all there is to it. What we’ve done is:

1. In state, grabbed the main canvas under the variable *currentCanvas*.
2. Put a listener for a click event on a Save button under the canvas that calls the *saveFractal()* function when activated.
3. Called *.toDataURL()* on the currentCanvas and assigned it to another variable *dataURL*.
4. POSTed the *dataURL* in a fetch to our back-end server under the *image* parameter, persisting the image in our database.

Now, if we look at our database being rendered as JSON through Chrome, we can see this data URL in its raw form:
<br/>

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/iou8zt5vfclwnuizdf0w.png)
<br/>

We have now successfully persisted a canvas in our back-end database as a PNG represented as an ASCII string.

But what if we want to render it back on our application? Thanks to the Data URL format technically being a URL, it’s as easy as working with any other URL for an image HTML tag.

Back in our front end, we can fetch the data for a saved fractal from the database, assign the data URL to a variable *image*, and render it on the page in a card like so:
<br/>

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/hcyoofscwm1e93utplt4.png)
<br/>

And voila!
<br/>

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/8dvk2vj4502f7nle0cpp.png)
<br/>

We’ve now successfully exported, persisted, retrieved, and rendered an HTML5 canvas as an image, all thanks to *.toDataURL()*.

***

One final point to cover, and one that might be even more useful than saving a canvas to a database, is the ability to directly export and save a canvas as an image to a user’s hard drive. Here we’ll use the same concept, but carry it out slightly differently.

Essentially what we’re doing here on our front end is creating an Export link (an \<a> tag, though it can be styled as a button) with a download attribute and a temporary HREF attribute, and adding a listener to it that calls a function with two actions:

1. Sets the canvas to a variable with *.toDataURL()*.
2. Changes the temporary HREF of the link to the Data URL that we’ve just encoded.

This is what it looks like:
<br/>

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/2dqausxue6w9dw6cbjto.png)
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/f6mv0dds2ah39u8q0xqe.png)
<br/>

Clicking this will open up a download window in your browser, with the value you assigned to the download attribute of the link being the temporary filename, like so:
<br/>

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/f6mv0dds2ah39u8q0xqe.png)
<br/>

And you’re done!

***

If you’ve come this far, you’ve learned how to use the built-in functionality of *.toDataURL()* to save an HTML5 canvas as an encoded string in a database and render it back on a page or to save it directly to a user’s hard drive. Using this can add some great interactivity and functionality to a web application.

Thanks for reading!