Use FileReader.readAsDataURL() for a browser File or Blob, canvas.toDataURL('image/png') for a canvas, and UTF-8 byte conversion before encoding HTML text. A data URL contains both a media-type prefix and the Base64 payload; keep the prefix for an <img> and remove it only when an API asks for raw Base64.
Choose the right Base64 form first
“Base64” can mean either the encoded payload alone or a complete data URL. The correct method depends on what you start with and what the next system accepts.
| Input | Environment | Recommended method | Result |
|---|---|---|---|
PNG File or Blob |
Browser | FileReader.readAsDataURL() |
data:image/png;base64,... |
| Canvas bitmap | Browser | canvas.toDataURL('image/png') |
PNG data URL |
| HTML text | Browser | UTF-8 bytes with TextEncoder, then btoa() |
Raw Base64 text |
| PNG bytes or HTML text | Node.js | Buffer methods |
Raw Base64 or decoded bytes |
For an HTML <img src>, use the complete data URL. For a JSON field or service that explicitly requests Base64, remove the prefix and send only the payload.
Convert a PNG File or Blob in the browser
Read a file input as a complete data URL
readAsDataURL() is asynchronous and accepts either a File or any Blob. The returned string includes the media type and the Base64 data.
#1 Best Overall
function blobToDataUrl(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = () => reject(reader.error);
reader.readAsDataURL(blob);
});
}
const input = document.querySelector('#png-file');
input.addEventListener('change', async () => {
const file = input.files[0];
if (!file) return;
const dataUrl = await blobToDataUrl(file);
document.querySelector('#preview').src = dataUrl;
});
With an input such as <input id="png-file" type="file" accept="image/png">, dataUrl starts with data:image/png;base64,. Assigning it directly to an image’s src preserves the form browsers expect.
Extract only the raw Base64 payload
Do not split at an arbitrary comma: a media type can contain parameters. Remove the complete declaration with a prefix-aware expression when the receiving API requires raw Base64.
const dataUrl = await blobToDataUrl(file);
const rawBase64 = dataUrl.replace(/^data:[^;]+;base64,/, '');
Keep dataUrl for a data-URL consumer and use rawBase64 only for an interface that documents that requirement. MDN specifically notes that the declaration must be removed before decoding the payload as Base64.
Rank #2
- HTML CSS Design and Build Web Sites
- Comes with secure packaging
- It can be a gift option
Turn a canvas image into PNG Base64
When the pixels are already in a canvas, serialize them directly. Browsers are required to support PNG, so this is the portable choice.
Recommended Free Tools
const pngDataUrl = canvas.toDataURL('image/png');
// pngDataUrl starts with data:image/png;base64,
The operation encodes the entire image into an in-memory string. A very large canvas can therefore consume substantial memory. If the canvas contains cross-origin content without appropriate CORS handling, serialization can fail with a security error because the canvas is not origin-clean. Make the source resources CORS-compatible before drawing them, or use same-origin assets.
Convert an HTML string to Base64 safely
ASCII-only HTML
btoa() works when every character in the string represents a single byte, which includes ASCII-only markup.
Rank #3
const html = '<h1>Hello</h1>';
const base64 = btoa(html);
const restored = atob(base64);
Unicode HTML, including emoji and non-Latin text
JavaScript strings are Unicode, but btoa() accepts a binary string whose characters represent byte values. Encode the text as UTF-8 bytes first, then construct the binary string expected by btoa().
function utf8ToBase64(text) {
const bytes = new TextEncoder().encode(text);
let binary = '';
for (const byte of bytes) binary += String.fromCodePoint(byte);
return btoa(binary);
}
function base64ToUtf8(base64) {
const binary = atob(base64);
const bytes = Uint8Array.from(binary, ch => ch.charCodeAt(0));
return new TextDecoder().decode(bytes);
}
const html = '<p>Café — こんにちは 🌍</p>';
const encoded = utf8ToBase64(html);
const decodedHtml = base64ToUtf8(encoded);
This byte-oriented path avoids the failure that occurs when arbitrary Unicode is passed directly to btoa(). atob() returns a binary string, so decode those byte values with TextDecoder to recover the original UTF-8 text.
Decode Base64 back into usable data
Decode a data URL in the browser
If you have a complete data URL, separate its metadata from the payload before decoding. The payload is the portion after the comma. For a binary image, turn the decoded bytes into a typed array rather than treating them as ordinary text.
Rank #4
- Brand: Wiley
- Set of 2 Volumes
- A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers
function dataUrlToBytes(dataUrl) {
const match = dataUrl.match(/^data:([^;]+);base64,(.*)$/);
if (!match) throw new Error('Expected a Base64 data URL');
const binary = atob(match[2]);
const bytes = Uint8Array.from(binary, ch => ch.charCodeAt(0));
return { mimeType: match[1], bytes };
}
const { mimeType, bytes } = dataUrlToBytes(dataUrl);
const pngBlob = new Blob([bytes], { type: mimeType });
Pass only the Base64 portion to atob(). Invalid input causes atob() to throw InvalidCharacterError.
Convert HTML and PNG data in Node.js
Node.js code should use Buffer.from(value, 'base64') to decode and buffer.toString('base64') to encode. Node documents these as the preferred API methods; browser-compatible btoa() and atob() functions are legacy choices for new Node API code.
Encode a PNG file and an HTML string
import { readFile } from 'node:fs/promises';
const pngBytes = await readFile('image.png');
const pngBase64 = pngBytes.toString('base64');
const html = '<main>Résumé — こんにちは</main>';
const htmlBase64 = Buffer.from(html, 'utf8').toString('base64');
console.log(pngBase64);
console.log(htmlBase64);
pngBase64 is the raw payload. To create a data URL for an image consumer, prepend data:image/png;base64, yourself.
Best Value
Decode Base64 in Node.js
const decodedPng = Buffer.from(pngBase64, 'base64');
await import('node:fs/promises').then(({ writeFile }) =>
writeFile('decoded.png', decodedPng)
);
const decodedHtml = Buffer.from(htmlBase64, 'base64').toString('utf8');
Keep binary results as a Buffer; convert to UTF-8 text only when the original data was text such as HTML.
Common failures and fixes
“Invalid character” from btoa()
- Cause: The input contains Unicode characters outside the byte range accepted by
btoa(). - Fix: Encode with
TextEncoderand use the UTF-8 helper above.
atob() throws InvalidCharacterError
- Cause: The input is not valid Base64, or it still includes the
data:...;base64,declaration. - Fix: Validate the value and remove the complete prefix before decoding.
The server rejects an uploaded image
- Cause: You sent a full data URL where the endpoint expects only the payload, or sent raw Base64 where it expects a data URL.
- Fix: Check the endpoint contract. Preserve
data:image/png;base64,for data-URL fields; strip it for raw-Base64 fields.
Canvas serialization raises a security error
- Cause: The canvas is not origin-clean because it contains cross-origin content without suitable CORS handling.
- Fix: Use same-origin resources or configure the image request and server response for CORS before drawing.
Large conversions use too much memory
- Cause:
toDataURL()and data URLs hold the complete encoded image in memory. - Fix: Avoid converting unnecessarily large canvases, release references after upload, and prefer a binary
Blobworkflow when the destination accepts one.
Practical decisions for production code
- Choose the representation at the boundary: data URL for an HTML image source; raw Base64 for a field that explicitly requests it; bytes or a
Bufferfor binary processing. - Preserve the media type: a data URL records the MIME type, while raw Base64 does not. Store or transmit the type separately when you remove the prefix.
- Keep encoding and decoding symmetrical: UTF-8 text must be decoded as UTF-8, and PNG bytes must remain binary.
- Expect asynchronous browser APIs: wait for
FileReadercompletion before reading its result. - Do not treat Base64 as a security layer: encoding changes representation; it does not provide access control or encryption. Protect sensitive HTML and image data using the transport and authorization controls of the system receiving it.
Or skip the browser setup
If your immediate goal is to obtain a PNG of a web page before encoding it, ScreenshotNeo returns a clean PNG, JPEG, WebP, or PDF from one GET request. It accepts cookie and consent banners as a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
One-call capture
See the full parameter reference in the ScreenshotNeo documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
The resulting image can then be read with FileReader in a browser or Buffer in Node.js and encoded using the recipes above.
Python
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)
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}`);
ScreenshotNeo includes 63 options, including full-page capture with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets plus custom viewports, retina scale, PDF paper and page controls, custom CSS and JavaScript, clicks before capture, hidden selectors, selector or network-idle waits, ad/tracker/request blocking, custom headers, cookies, user agent and authorization, timezone and geolocation, transparent backgrounds, resizing, selectable cache TTLs, signed links, asynchronous jobs with signed webhooks, bulk capture for 100 URLs per call, a usage API, and an OpenAPI specification. Parameter names used by other screenshot APIs also work, which can simplify migration.
| Plan | Included shots | Price |
|---|---|---|
| Free | 1,000 per month | $0, no card |
| Starter | 3,000 | $5 |
| Growth | 15,000 | $15 |
| Pro | 60,000 | $39 |
| Scale | 250,000 | $99 |
| Business | 1,000,000 | $249 |
Every feature is available on every plan, and yearly billing gives two months free. The paid entry point is $5 for 3,000 shots, while the free plan provides 1,000 shots monthly without a card. Create a free ScreenshotNeo account to capture the image you need before converting it to Base64.
Quick Recap
Quick decision checklist
- Have a browser file or Blob? Use
readAsDataURL(). - Have a canvas? Use
toDataURL('image/png'), and account for origin-clean and memory requirements. - Have HTML text? Use UTF-8 byte conversion unless it is strictly ASCII.
- Running Node.js? Use
Bufferfor both encoding and decoding. - Need an image of a live page first? Capture it, then apply the same PNG or binary conversion path.
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.

