Question

Export HTML Canvas to PNG, JPEG, GIF, and PDF with JavaScript

htmljavascriptcanvasexportpng

Question

Is it possible to capture or print the content displayed in an HTML <canvas> element as an image or PDF? For example, after generating an image with canvas, how can I export it as a PNG file?

Short Answer

You will learn how canvas pixels can be exported as image data, downloaded as PNG or JPEG files, displayed in an <img> element, and placed into a PDF. You will also learn why GIF export is different and why cross-origin images can prevent exporting.

Concept

An HTML <canvas> is a drawing surface managed by the browser. Although it may contain shapes, text, or images, the canvas itself is not automatically an image file.

JavaScript can ask the browser to encode the current canvas pixels into an image format:

  • canvas.toDataURL() returns a Base64 data URL string.
  • canvas.toBlob() creates a Blob, which is efficient for downloads, uploads, and object URLs.

PNG is usually the best default because it is lossless and supports transparency. JPEG is smaller for photographic content but does not support transparency. Creating a PDF is normally a separate step: export or use the canvas image data, then insert it into a PDF using a client-side PDF library or generate the PDF on a server.

This matters in applications that create charts, signatures, certificates, image editors, reports, diagrams, and downloadable user-generated content.

Mental Model

Think of a canvas as a whiteboard in a browser.

  • Drawing methods such as fillRect() and fillText() put marks on the whiteboard.
  • toBlob() takes a photograph of the whiteboard and puts that photograph into a file-like container.
  • toDataURL() takes the same photograph but writes it as a long text string.
  • A PDF is like placing that photograph onto a page in a document.

The export captures the canvas as it looks at that moment. Later changes to the canvas are not included in a file that was already exported.

Take Quiz

Syntax and Examples

Use toBlob() when you want a downloadable file or need to upload the result. It avoids creating a large Base64 string in JavaScript memory.

const canvas = document.querySelector("canvas");

canvas.toBlob((blob) => {
  if (!blob) {
    console.error("Canvas export failed.");
    return;
  }

  const url = URL.createObjectURL(blob);
  const link = document.createElement("a");

  link.href = url;
  link.download = "drawing.png";
  link.click();

  URL.revokeObjectURL(url);
}, "image/png");

The second argument is the MIME type. Common choices are:

"image/png"       // lossless; supports transparency
"image/jpeg"      // smaller for photos; no transparency
"image/webp"      // supported by many modern browsers

For JPEG or WebP, a third argument can set approximate quality from 0 to 1:

canvas.toBlob(saveFile, "image/jpeg", 0.9);

function saveFile(blob) {
  // Use the Blob here.
}

toDataURL() is useful when an API or library explicitly needs a data URL:

const pngDataUrl = canvas.toDataURL("image/png");
console.log(pngDataUrl);

const preview = document.createElement("img");
preview.src = pngDataUrl;
document.body.append(preview);

A data URL begins with text similar to data:image/png;base64,.... It contains the complete image encoded as a string.

Step by Step Execution

Consider this complete example:

<canvas id="art" width="400" height="200"></canvas>
<button id="download">Download PNG</button>

<script>
  const canvas = document.querySelector("#art");
  const context = canvas.getContext("2d");

  context.fillStyle = "#2563eb";
  context.fillRect(0, 0, canvas.width, canvas.height);

  context.fillStyle = "white";
  context.font = "32px sans-serif";
  context.fillText("Canvas export", 85, 115);

  document.querySelector("#download").addEventListener("click", () => {
    canvas.toBlob((blob) => {
      if (!blob) return;

      const url = URL.createObjectURL(blob);
      const link = document.createElement("a");
      link.href = url;
      link.download = "canvas-art.png";
      link.click();
      URL.revokeObjectURL(url);
    }, "image/png");
  });
</script>

Execution flow:

  1. The <canvas> element creates a 400 by 200 pixel drawing area.
  2. getContext("2d") provides the 2D drawing API.
  3. fillRect() paints a blue background.
  4. fillText() draws white text over the background.
  5. When the button is clicked, toBlob() encodes the current pixels as a PNG.
  6. URL.createObjectURL() creates a temporary browser URL for that PNG blob.
  7. A temporary <a> element with download triggers the file download.
  8. URL.revokeObjectURL() releases the temporary URL after it is no longer needed.

Real World Use Cases

Canvas export is common in many browser applications:

  • Chart dashboards: Let users download a rendered chart as a PNG for a presentation.
  • Signature pads: Save a handwritten signature as a PNG and upload it with a form.
  • Image annotation tools: Export an image after users add labels, arrows, or highlights.
  • Report generators: Render a chart or diagram and insert it into a PDF report.
  • Drawing applications: Save a user's sketch locally or send it to an API.
  • Certificate generators: Draw personalized certificate text and decorations, then download the result as an image or PDF.
Take Quiz

Real Codebase Usage

In production code, exporting is often wrapped in a reusable function and paired with validation and error handling.

Reusable PNG download helper

function downloadCanvas(canvas, filename = "image.png") {
  if (!(canvas instanceof HTMLCanvasElement)) {
    throw new TypeError("A canvas element is required.");
  }

  canvas.toBlob((blob) => {
    if (!blob) {
      console.error("The browser could not create the image blob.");
      return;
    }

    const link = document.createElement("a");
    const url = URL.createObjectURL(blob);

    link.href = url;
    link.download = filename;
    link.click();

    setTimeout(() => URL.revokeObjectURL(url), 0);
  }, "image/png");
}

Uploading the canvas to an API

Use FormData when the server expects a file upload:

async function uploadCanvas(canvas) {
  const blob = await new Promise((resolve) => {
    canvas.toBlob(resolve, "image/png");
  });

  if (!blob) {
    throw new Error("Could not export canvas.");
  }

  const formData = new FormData();
  formData.append("image", blob, "drawing.png");

  const response = await fetch("/api/images", {
    method: "POST",
    body: formData
  });

  if (!response.ok) {
    throw new Error("Image upload failed.");
  }
}

Creating a PDF

A canvas does not directly become a PDF with the standard Canvas API. A common client-side approach is to use a PDF library, such as jsPDF, and add the canvas image to a page:

// Assumes jsPDF has been loaded by your application.
const { jsPDF } = window.jspdf;
const pdf = new jsPDF({ orientation: "landscape" });
const image = canvas.toDataURL("image/png");

pdf.addImage(image, "PNG", 10, 10, 270, 135);
pdf.save("report.pdf");

For reports with complex layouts, pagination, or reliable printing, applications may instead send the data to a server that generates the PDF.

Common Mistakes

Assuming canvas export includes surrounding HTML

Only pixels drawn inside the canvas are exported. Buttons, labels, and other DOM elements around it are not included.

canvas.toDataURL(); // Exports only the canvas, not the entire page.

If you need a full page or component capture, use a dedicated DOM-to-image approach or generate a print-specific layout.

Exporting before an image finishes loading

This can produce an empty or incomplete result.

const image = new Image();
image.src = "/photo.png";
context.drawImage(image, 0, 0); // Too early: the image may not be loaded yet.

Wait for onload instead:

const image = new Image();
image.onload = () => {
  context.drawImage(image, 0, 0);
  canvas.toBlob((blob) => {
    // Export after drawing is complete.
  }, "image/png");
};
image.src = "/photo.png";

Ignoring cross-origin security restrictions

If you draw an image from another origin without suitable CORS permission, the canvas becomes tainted. Calls such as toDataURL() and toBlob() may throw a SecurityError or fail.

When the remote server permits CORS, set crossOrigin before setting src:

const image = new Image();
image.crossOrigin = "anonymous";
image.onload = () => context.drawImage(image, 0, 0);
image.src = "https://example.com/public-image.png";

The image server must also send an appropriate Access-Control-Allow-Origin header. You cannot fix this only from browser JavaScript if the server does not allow it.

Expecting JPEG to preserve transparency

JPEG has no alpha channel. Transparent regions are flattened, often against black or another browser-chosen background. Use PNG when transparency matters.

Using GIF as a normal canvas export format

Browsers commonly support PNG and JPEG canvas encoding, and may support WebP. GIF is not a standard output type for toBlob() or toDataURL(). Creating an animated GIF typically requires collecting frames and encoding them with a GIF encoder library or on a server.

Forgetting the canvas resolution

The export size comes from canvas.width and canvas.height, not the CSS display size. A canvas displayed at 800 CSS pixels wide can still export at only 400 pixels wide if its internal width is 400.

Comparisons

OptionResultBest useImportant note
canvas.toBlob()Blob asynchronouslyDownloads, uploads, large imagesPreferred default for file workflows
canvas.toDataURL()Base64 string synchronouslyImage previews and libraries requiring data URLsCan use substantial memory for large canvases
PNGLossless imageText, diagrams, transparency, drawingsUsually larger than JPEG for photos
JPEGLossy imagePhotos where small files matterDoes not support transparency
WebPModern image formatSmaller image files when supported by your target browsersVerify browser and backend support
GIFStatic or animated imageLegacy/simple animationsNot a standard canvas export output format
PDFDocument containing image(s)Reports, printing, multi-page documentsUsually requires a PDF library or server-side generator

toBlob() and toDataURL() both capture the canvas pixels. The main difference is the form of the result: a file-like binary object versus a text string.

Take Quiz

Cheat Sheet

// PNG Blob: preferred for download/upload
canvas.toBlob((blob) => {
  // blob may be null, so check it.
}, "image/png");

// JPEG Blob with quality
canvas.toBlob(callback, "image/jpeg", 0.85);

// Base64 data URL
const url = canvas.toDataURL("image/png");

// Download a Blob
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = "image.png";
link.click();
URL.revokeObjectURL(url);
  • Use PNG for transparency, text, screenshots, and diagrams.
  • Use JPEG for smaller photographic images; quality is usually between 0 and 1.
  • Prefer toBlob() for downloads and API uploads.
  • Canvas export captures only the canvas bitmap, not nearby HTML.
  • Wait until all drawing and image loading is complete before exporting.
  • Cross-origin images need server-approved CORS headers, or export is blocked.
  • GIF encoding is not a standard toBlob()/toDataURL() output feature.
  • The internal canvas dimensions control output resolution.

FAQ

How do I save an HTML canvas as a PNG?

Call canvas.toBlob() with "image/png", create an object URL from the blob, and trigger a download with an <a> element.

Is toBlob() better than toDataURL()?

For downloads and uploads, usually yes. toBlob() produces a file-like binary object and does not require keeping a large Base64 string in memory.

Can I convert a canvas to JPEG?

Yes. Use canvas.toBlob(callback, "image/jpeg", quality) or canvas.toDataURL("image/jpeg", quality). JPEG does not retain transparent pixels.

Why does canvas export fail after I draw an external image?

The external image may not have CORS permission. A canvas that contains unapproved cross-origin image data cannot be read back by JavaScript for security reasons.

Can a canvas be exported as an animated GIF?

Not directly with the standard Canvas API. You need to capture multiple canvas frames and use a GIF encoding library or a server-side encoder.

Can I create a PDF from a canvas?

Yes, but usually by adding the exported canvas image to a PDF with a library such as jsPDF, or by sending the image/data to a server-side PDF generator.

Why is my exported image blurry?

The canvas internal width and height may be too small. Increase those pixel dimensions before drawing; CSS size alone does not increase export resolution.

Take Quiz

Mini Project

Description

Build a small canvas badge generator. It draws a personalized badge and lets the user download the generated design as a PNG. This mirrors features used in certificate tools, social-media image generators, and simple design applications.

Goal

Create a canvas drawing and export its current contents as a downloadable PNG file.

Requirements

Draw a colored background on a canvas. Add a title and a user-provided name. Redraw the canvas when the user clicks a Generate button. Download the current canvas as a PNG when the user clicks Download PNG. Handle a failed blob export safely.

Take Quiz

Keep learning

Related questions

View more