Use HTMLCanvasElement.toBlob() to encode a canvas as a PNG, create a temporary object URL for the resulting Blob, and trigger a download link. This asynchronous approach avoids building a large in-memory data URL and is the practical default for downloadable images.
The reliable canvas-to-PNG recipe
The complete operation has four steps: serialize the canvas, create an object URL, attach it to an anchor, and revoke the URL after the browser has started the download.
function downloadCanvasAsPng(canvas, filename = "canvas.png") {
canvas.toBlob((blob) => {
if (!blob) {
throw new Error("Canvas could not be encoded.");
}
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = filename;
link.click();
// Let the browser begin the download before releasing the URL.
setTimeout(() => URL.revokeObjectURL(url), 0);
}, "image/png");
}
Call it with the canvas element you want to save:
const canvas = document.querySelector("#drawing");
downloadCanvasAsPng(canvas, "my-drawing.png");
toBlob() runs asynchronously and passes the encoded Blob to its callback. The callback receives null when an image cannot be created. Serialization can also throw a SecurityError when the canvas is not origin-clean; the cross-origin section below explains that case.
A complete runnable example
This page creates a canvas, draws a simple scene, and adds a button that downloads the bitmap as PNG. The element’s bitmap dimensions are set in HTML, so the saved file is 800 by 450 pixels regardless of how CSS displays it.
Recommended Free Tools
#1 Best Overall
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<title>Canvas PNG export</title>
<style>
canvas { width: 400px; border: 1px solid #999; }
</style>
<canvas id="drawing" width="800" height="450"></canvas>
<button id="save" type="button">Save PNG</button>
<script>
const canvas = document.querySelector("#drawing");
const context = canvas.getContext("2d");
context.fillStyle = "#152238";
context.fillRect(0, 0, canvas.width, canvas.height);
context.fillStyle = "#52d1dc";
context.beginPath();
context.arc(400, 225, 120, 0, Math.PI * 2);
context.fill();
context.fillStyle = "white";
context.font = "bold 42px sans-serif";
context.textAlign = "center";
context.fillText("Canvas PNG", 400, 235);
function downloadCanvasAsPng(canvas, filename = "canvas.png") {
canvas.toBlob((blob) => {
if (!blob) throw new Error("Canvas could not be encoded.");
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = filename;
link.click();
setTimeout(() => URL.revokeObjectURL(url), 0);
}, "image/png");
}
document.querySelector("#save").addEventListener("click", () => {
downloadCanvasAsPng(canvas, "canvas.png");
});
</script>