Driver FixRecommendedSound, Wi-Fi or graphics acting up? Check drivers firstFind missing or outdated drivers fast.Check DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PC×
Skip to content
Base64

How to Decode and Save an Image with canvas.toDataURL()

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

canvas.toDataURL() turns the current HTML canvas bitmap into a data: URL. To save a small PNG or JPEG, assign that URL to an anchor’s href and trigger a download. To decode the string for upload or further processing, split it at the first comma, read the MIME type from the header, and Base64-decode the payload. For large canvases, use canvas.toBlob() and URL.createObjectURL() instead: it avoids keeping the entire encoded image in one JavaScript string.

What canvas.toDataURL() returns

Calling canvas.toDataURL() serializes the canvas’s current pixels as a data: URL:

const dataUrl = canvas.toDataURL('image/png');
console.log(dataUrl); // data:image/png;base64,...

The default format is PNG. You may request another format, such as JPEG, by passing a supported MIME type. For lossy formats, the optional quality argument is a number from 0 to 1; for example, 0.85 requests approximately 85-percent quality. If the browser does not support the requested type, it falls back to PNG, so inspect the returned prefix when the exact format matters. Formats that support resolution metadata are exported at 96 dpi.

Draw first, export second

The method captures the bitmap at the moment it is called. A minimal complete example is:

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Sale
Kodak PIXPRO FZ45 Digital Camera, 16MP Point & Shoot (Black)
  • 16MP Sensor: Captures detailed photos with a CMOS sensor for everyday shooting
  • Optical Zoom: 4x optical zoom with a 27mm wide angle lens for flexible framing indoors or outdoors
  • Full HD Video: Records 1080p video for travel clips, family moments, or simple vlogging
  • Memory Support: Works with Class 10 SD, SDHC, or SDXC cards up to 512GB
  • LCD Screen and Battery: 2.7in LCD screen with 2 AA alkaline batteries for convenient on-the-go use
<canvas id="art" width="800" height="450"></canvas>
<script>
  const canvas = document.querySelector('#art');
  const ctx = canvas.getContext('2d');
  ctx.fillStyle = '#1769aa';
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  ctx.fillStyle = 'white';
  ctx.font = '32px sans-serif';
  ctx.fillText('Saved canvas', 40, 80);

  const pngDataUrl = canvas.toDataURL('image/png');
  console.log(pngDataUrl.slice(0, 30));
</script>

Canvas dimensions are bitmap dimensions, not merely CSS display dimensions. Set the width and height attributes (or the corresponding properties) before drawing if you need a particular output size.

Download a canvas as PNG or JPEG

For a small export, create an anchor, set its download filename and use the data URL as href:

function downloadCanvas(canvas, type = 'image/png', quality) {
  const extension = type === 'image/jpeg' ? 'jpg' :
                    type === 'image/webp' ? 'webp' : 'png';
  const dataUrl = canvas.toDataURL(type, quality);
  const link = document.createElement('a');
  link.download = `canvas-image.${extension}`;
  link.href = dataUrl;
  link.click();
}

downloadCanvas(document.querySelector('#art'), 'image/png');
downloadCanvas(document.querySelector('#art'), 'image/jpeg', 0.85);

The browser may apply its own download or security policy, particularly when the call is not made as a result of a user gesture. Invoke the function from a click handler for the most consistent behavior:

document.querySelector('#save').addEventListener('click', () => {
  downloadCanvas(document.querySelector('#art'), 'image/png');
});

PNG preserves transparency and is a good default for drawings, text and screenshots. JPEG is smaller for photographic content but has no alpha channel; transparent pixels are typically composited against a background. WebP can be requested where supported, but always check the returned MIME prefix if your application depends on it.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Decode a data URL into a Blob

A data URL has a metadata header, a comma, and the encoded data. A Base64 example looks like data:image/png;base64,iVBORw0KGgo.... Do not pass the complete string directly to atob(); remove the header first. The following function handles both Base64 and percent-encoded data URLs:

function dataUrlToBlob(dataUrl) {
  const [header, payload] = dataUrl.split(',', 2);
  if (!header || payload === undefined || !header.startsWith('data:')) {
    throw new Error('Invalid data URL');
  }

  const mime = /data:([^;]+)/.exec(header)?.[1] ||
               'application/octet-stream';

  if (header.includes(';base64')) {
    const binary = atob(payload);
    const bytes = Uint8Array.from(binary, character => character.charCodeAt(0));
    return new Blob([bytes], { type: mime });
  }

  const bytes = new TextEncoder().encode(decodeURIComponent(payload));
  return new Blob([bytes], { type: mime });
}

const dataUrl = canvas.toDataURL('image/png');
const blob = dataUrlToBlob(dataUrl);
console.log(blob.type, blob.size);

Blob.type reports the MIME type from the header, while Blob.size is the decoded byte count. You can upload the Blob with fetch or display it through an object URL:

const imageUrl = URL.createObjectURL(blob);
const preview = document.querySelector('#preview');
preview.src = imageUrl;

// Revoke it after the image has loaded and is no longer needed.
preview.addEventListener('load', () => URL.revokeObjectURL(imageUrl), { once: true });

Object URLs point to in-memory Blob data. Revoke them when the consumer is finished; otherwise a long-running page can retain memory unnecessarily.

Rank #2
Sale
Kodak PIXPRO FZ55-BK 16MP CMOS Sensor Camera 5X Optical Zoom 28mm Wide
  • 16MP Sensor: Captures detailed photos with a CMOS sensor for everyday shooting
  • Optical Zoom: 5x optical zoom with a 28mm wide angle lens for flexible framing indoors or outdoors
  • Full HD Video: Records 1080p video for travel clips, family moments, or simple vlogging
  • Memory Support: Works with Class 10 SD, SDHC, or SDXC cards up to 512GB
  • LCD Screen and Battery: 2.7in LCD screen and a rechargeable lithium-ion battery for on-the-go use

Decode a FileReader data URL

FileReader.readAsDataURL() uses the same structure. The header can contain a MIME type and the ;base64 marker. Decode only the portion after the first comma:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const input = document.querySelector('#file');
input.addEventListener('change', () => {
  const file = input.files[0];
  if (!file) return;

  const reader = new FileReader();
  reader.addEventListener('load', () => {
    const dataUrl = reader.result;
    const blob = dataUrlToBlob(dataUrl);
    console.log(blob.type, blob.size);
  });
  reader.readAsDataURL(file);
});

Splitting with split(',', 2) is intentional: it preserves the remainder as the payload. Percent-encoded, non-Base64 data must be decoded with decodeURIComponent; Base64 data must go through atob.

Use toBlob() for large images

toDataURL() constructs the complete encoded image in an in-memory JavaScript string. That can temporarily require substantially more memory than the final file, and very large strings can hit browser limits. MDN recommends toBlob() with URL.createObjectURL() instead.

function downloadCanvasBlob(canvas, type = 'image/png', quality) {
  const extension = type === 'image/jpeg' ? 'jpg' :
                    type === 'image/webp' ? 'webp' : 'png';

  canvas.toBlob((blob) => {
    if (!blob) {
      console.error('The browser could not encode this canvas.');
      return;
    }
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.download = `canvas-image.${extension}`;
    link.href = url;
    link.click();
    URL.revokeObjectURL(url);
  }, type, quality);
}

downloadCanvasBlob(canvas, 'image/png');

The callback can receive null, so handle that case. Revoke the object URL after the download has been initiated; if you keep the URL in an image, editor or preview, wait until that interaction ends before revoking it.

Upload without a Base64 round trip

canvas.toBlob(async (blob) => {
  if (!blob) throw new Error('Encoding failed');
  const response = await fetch('/api/images', {
    method: 'POST',
    headers: { 'Content-Type': blob.type || 'image/png' },
    body: blob
  });
  if (!response.ok) throw new Error(`Upload failed: ${response.status}`);
}, 'image/png');

This avoids embedding binary data in JSON and generally keeps memory use lower than creating a data URL first.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Why a canvas becomes tainted

A canvas is origin-clean only when every pixel source is permitted by the browser’s same-origin rules. If you draw an image, video or another canvas from a different origin without suitable CORS headers, the canvas becomes tainted. Reading pixels or exporting then throws a SecurityError.

try {
  const dataUrl = canvas.toDataURL('image/png');
} catch (error) {
  if (error.name === 'SecurityError') {
    console.error('The canvas contains cross-origin pixels without CORS.');
  }
  throw error;
}

For an image you control, request CORS before assigning src, and configure the image server to return an appropriate Access-Control-Allow-Origin response:

Rank #3
Sale
Digital Camera, Latest FHD 1080P Digital Camera for Teens with SD Card Anti Shake Point and Shoot Cameras Portable 16X Zoom Compact Small Cameras for Kids Boys Girls Seniors with Wrist Strap
  • Latest Digital Camera Built-in Fill Light : This compact digital camera is paired with a powerful CMOS processor and image stabilization to help you take & record the most exciting moments in 44 MP quality images & FHD 1080P quality videos anywhere, anytime. Plus, there is also a built-in fill light to help you take high quality pictures even in low light&dark settings, making this the perfect camera for all indoors/outdoors situations.
  • Long-Lasting Battery Life & 16X Digital Zoom :This point and shoot camera will retain its battery charge even after long use. The controls and functions are easy to operate making this the perfect choice for children, teens and younger. This kids camera supports 16x digital zoom, you can zoom in or out the subject by pressing the W/T button for taking still photos to zoom in or out on distant objects and capture all the details you need.
  • Multifunctional & Portable Digital Camera: This cheap digital camera is slim enough to fit in your pocket. You'll easily be able to take it with you on all your indoor/outdoor activities and adventures and ideal for beginners, children and teenagers. This kids digital camera is equipped with 20 filters, anti-shaking, self-timer, continuous shooting, date stamp, time-lapse recording, smile capture, internal MIC and speaker (recording sound videos), great for your daily photography needs.
  • WEBCAM & PAUSE FUNCTION : More than just a FHD 1080p digital camera, it also works as a webcam for video calls and vlogging. Connect the camera to the computer, press shutter and power button at the same time and the camera will automatically turn on webcam mode for all your video calling and live streaming needs. The pause function allows you to pause when seeing playback videos.
  • A Must Have Photography Device : This digital camera with SD card made from high-quality materials, this retro camera is safe and durable. Perfect for all ages to develop & improve their photographic abilities and observation skills. Our dedicated and experienced 24/7 support team is available for all after purchase troubleshooting, questions and technical help.
const image = new Image();
image.crossOrigin = 'anonymous';
image.onload = () => {
  ctx.drawImage(image, 0, 0);
  const png = canvas.toDataURL('image/png');
};
image.onerror = () => console.error('Image could not be loaded with CORS');
image.src = 'https://images.example.com/photo.jpg';

Setting crossOrigin after src is too late. The server must also opt in; a client-side flag cannot override a server that sends no CORS permission. If you do not control the remote server, proxy the resource through a server you control, subject to that resource’s terms and access controls. Do not attempt to bypass authentication or anti-forgery protections.

Empty output, unsupported formats and size limits

The returned value is data:,

A zero-sized canvas returns data:,. The same result is returned when the canvas exceeds the browser’s maximum canvas dimensions. Check dimensions before drawing and design large exports in tiles or at a reduced scale:

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
if (canvas.width === 0 || canvas.height === 0) {
  throw new Error('Canvas has no drawable area');
}
const result = canvas.toDataURL('image/png');
if (result === 'data:,') {
  throw new Error('Canvas dimensions exceed this browser limit');
}

Maximum dimensions vary by browser and device, so do not hard-code a universal limit. A large CSS element can also require more bitmap pixels than expected when you use a high device-pixel ratio; calculate the requested bitmap size deliberately.

The browser returns PNG despite your request

Unsupported MIME types fall back to PNG. Test the prefix:

const requested = canvas.toDataURL('image/webp', 0.8);
if (!requested.startsWith('data:image/webp;')) {
  console.warn('WebP was not supported; the result may be PNG.');
}

JPEG looks black or loses transparency

JPEG has no alpha channel. Paint an opaque background before exporting if transparent pixels should appear white (or another chosen color):

const copy = document.createElement('canvas');
copy.width = canvas.width;
copy.height = canvas.height;
const copyCtx = copy.getContext('2d');
copyCtx.fillStyle = '#fff';
copyCtx.fillRect(0, 0, copy.width, copy.height);
copyCtx.drawImage(canvas, 0, 0);
const jpeg = copy.toDataURL('image/jpeg', 0.85);

Performance and reliability checklist

  • Use toDataURL() for small, immediate previews or downloads where a string is convenient.
  • Use toBlob() plus an object URL for large images, repeated exports and uploads.
  • Keep the canvas origin-clean by setting crossOrigin before src and configuring the image server’s CORS response.
  • Call export after fonts, images and other asynchronous drawing operations have completed; otherwise the bitmap may be incomplete.
  • Check for data:,, a null Blob, unsupported MIME fallback and SecurityError.
  • Revoke object URLs when previews, downloads or editing sessions no longer need them.
  • Choose PNG for transparency and crisp graphics; choose JPEG with a measured quality value for photographs.
  • Use a user-initiated click for downloads, because browsers can restrict synthetic downloads outside user interaction.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If what you really need is a clean screenshot of a web page rather than pixels from a canvas you already control, ScreenshotNeo provides a website screenshot API and MCP server. It accepts cookie and consent banners before capture, removes more than 60 known consent platforms plus newsletter popups and chat widgets, and bills only clean shots. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed; each response identifies the page verdict and billing status in X-Page-Verdict and X-Billed headers.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

One GET request returns PNG, JPEG, WebP or PDF. The full option set includes full-page capture with lazy images loaded, CSS-selector element capture, dark mode, device presets and custom viewports, retina scale, PDF paper and page controls, custom CSS and JavaScript, pre-capture clicks, hidden selectors, selector/delay/network-idle waits, request and resource blocking, custom headers/cookies/user agent/Authorization, timezone and geolocation, transparent backgrounds, resizing, selectable-TTL caching, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API and an OpenAPI specification. Parameters used by other screenshot APIs also work, which can simplify migration.

For AI workflows, its MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients.

Rank #4
Kodak PIXPRO FZ55-RD 16MP Camera 5X Optical Zoom 28mm Wide Angle 1080p
  • 16MP Sensor: Captures detailed photos with a CMOS sensor for everyday shooting
  • Optical Zoom: 5x optical zoom with a 28mm wide angle lens for flexible framing indoors or outdoors
  • Full HD Video: Records 1080p video for travel clips, family moments, or simple vlogging
  • Memory Support: Works with Class 10 SD, SDHC, or SDXC cards up to 512GB
  • LCD Screen and Battery: 2.7in LCD screen and a rechargeable lithium-ion battery for on-the-go use

See the ScreenshotNeo documentation for authentication and options. A direct cURL call is:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

The equivalent Python request is:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

And Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

The Free plan includes 1,000 shots per month without a card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan, and yearly billing provides two months free. Create a free ScreenshotNeo account to try it.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Frequently encountered errors

SecurityError: The operation is insecure

One or more drawn resources are cross-origin without CORS. Set crossOrigin before loading, configure the server, or use a permitted same-origin proxy.

InvalidCharacterError from atob()

You passed the data: header or a non-Base64 payload to atob. Split at the first comma and check for ;base64 first.

The download is tiny, blank or stale

Export only after drawing and asynchronous image/font loading finish. Confirm the canvas dimensions and inspect the bitmap in a preview before saving.

The tab becomes unresponsive

The encoded string is too large for a practical data-URL workflow. Switch to toBlob(), reduce bitmap dimensions, export less frequently, and release object URLs when finished.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Frequently Asked Questions

Does canvas.toDataURL() return Base64 only?

No. It returns a complete data URL. The Base64 payload follows the comma only when the header contains ;base64; the header also identifies the MIME type.

Can I decode a data URL with fetch()?

Yes. fetch(dataUrl) can produce a Blob, but splitting and decoding the URL yourself gives explicit control over MIME validation and malformed input handling.

What resolution does a canvas export have?

The pixel dimensions are the canvas bitmap’s width and height. Formats that carry resolution metadata use 96 dpi; that metadata does not add pixels.

Quick Recap

SaleBestseller No. 1
Kodak PIXPRO FZ45 Digital Camera, 16MP Point & Shoot (Black)
Kodak PIXPRO FZ45 Digital Camera, 16MP Point & Shoot (Black)
16MP Sensor: Captures detailed photos with a CMOS sensor for everyday shooting; Full HD Video: Records 1080p video for travel clips, family moments, or simple vlogging
$99.99
SaleBestseller No. 2
Kodak PIXPRO FZ55-BK 16MP CMOS Sensor Camera 5X Optical Zoom 28mm Wide
Kodak PIXPRO FZ55-BK 16MP CMOS Sensor Camera 5X Optical Zoom 28mm Wide
16MP Sensor: Captures detailed photos with a CMOS sensor for everyday shooting; Full HD Video: Records 1080p video for travel clips, family moments, or simple vlogging
$139.99
Bestseller No. 4
Kodak PIXPRO FZ55-RD 16MP Camera 5X Optical Zoom 28mm Wide Angle 1080p
Kodak PIXPRO FZ55-RD 16MP Camera 5X Optical Zoom 28mm Wide Angle 1080p
16MP Sensor: Captures detailed photos with a CMOS sensor for everyday shooting; Full HD Video: Records 1080p video for travel clips, family moments, or simple vlogging
$139.99

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Read next

Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.