In a browser, convert an image URL to a complete Base64 data URL with fetch(), Response.blob(), and FileReader.readAsDataURL(). The result looks like data:image/jpeg;base64,... and can be assigned directly to img.src. If another API wants only the Base64 characters, remove everything through the first comma.
The standard browser conversion
This function downloads the image, verifies the HTTP response, reads the bytes as a Blob, and asks FileReader to create a data URL. It preserves the downloaded representation rather than drawing the image onto a canvas or re-encoding it.
async function imageUrlToDataUrl(url) {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Image request failed: ${response.status} ${response.statusText}`);
}
const blob = await response.blob();
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.addEventListener('load', () => resolve(reader.result), { once: true });
reader.addEventListener('error', () => reject(reader.error), { once: true });
reader.readAsDataURL(blob);
});
}
const dataUrl = await imageUrlToDataUrl('https://example.com/photo.jpg');
console.log(dataUrl);
dataUrl is a string containing the media declaration, a comma, and the encoded payload. The declaration is generated from the Blob’s MIME type, so it may be image/jpeg, image/png, image/webp, or another type returned by the server. Do not force image/png unless you actually converted the image to PNG.
Use the result as an image source
const img = document.querySelector('#preview');
img.src = dataUrl;
A data URL is self-contained, so the browser does not need to make a second request for the image. It is useful for previews, inline HTML, or APIs that explicitly accept data URLs.
#1 Best Overall
Return only raw Base64
Some APIs require the payload without data:image/...;base64,. Strip the prefix only at that boundary:
function dataUrlToBase64(dataUrl) {
return dataUrl.slice(dataUrl.indexOf(',') + 1);
}
const base64 = dataUrlToBase64(dataUrl);
console.log(base64);
Keep the full data URL when the consumer expects an img source or another data-URL value. Removing the prefix too early loses the MIME declaration that tells a consumer what the bytes represent.
Cross-origin images and CORS
The most common failure is not JavaScript syntax; it is the browser’s same-origin policy. A script can fetch an image hosted on another origin only when that server’s response permits the requesting origin with appropriate CORS headers.
An image loading successfully in ordinary HTML does not prove that JavaScript may read its bytes. The <img> element can display many cross-origin resources, while fetch() still rejects a response that lacks CORS permission. Client-side code cannot manufacture that permission. If you control the image server, configure its CORS policy for the site that runs this code. Otherwise, use a server-side request you control or obtain an endpoint that intentionally grants access.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Do not treat a public CORS proxy as a general solution. It adds a third party that can observe, alter, rate-limit, or discontinue access to the image, and it does not change the image owner’s CORS policy.
Diagnose a CORS failure
- Open the browser developer tools and inspect the failed request in the Network panel.
- Check whether the response contains an
Access-Control-Allow-Originvalue matching your page’s origin (or an appropriate permitted value). - Confirm that the URL is an image endpoint, not an HTML login page, redirect, or hotlink-protection response.
- If the server cannot be changed, move the download to your own backend and return the bytes from an endpoint designed for your application.
Canvas: use it when you need a rendered or transformed image
Canvas is an alternative when the image is already displayed in the page or you need to resize, crop, draw, or otherwise transform it. It does not simply expose the original response bytes: drawing and exporting can change the representation, metadata, and format.
Rank #2
function imageElementToDataUrl(img, type = 'image/png', quality) {
const canvas = document.createElement('canvas');
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
const context = canvas.getContext('2d');
if (!context) throw new Error('Could not create a 2D canvas context');
context.drawImage(img, 0, 0);
return canvas.toDataURL(type, quality);
}
const existingImage = document.querySelector('#source');
const renderedDataUrl = imageElementToDataUrl(existingImage, 'image/jpeg', 0.85);
For a remote image, set crossOrigin before assigning src, and make sure the host grants CORS access:
const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = () => {
document.querySelector('#preview').src = imageElementToDataUrl(img);
};
img.onerror = () => console.error('The image could not be loaded');
img.src = 'https://example.com/photo.jpg';
If foreign-origin pixels are drawn without the required permission, the canvas becomes tainted. Reading it with toDataURL() then throws a SecurityError. Setting crossOrigin is necessary but not sufficient; the remote response must also contain the matching CORS approval.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Repair Windows errors before they cause bigger problems3Scan for outdated or missing drivers - takes under a minutetoDataURL() versus toBlob()
toDataURL() returns an image data URL. If the type is omitted or unsupported, the canvas export defaults to PNG. The entire encoded image is held in an in-memory string, which can be expensive for large images. For large output, MDN generally recommends toBlob() together with URL.createObjectURL() instead of building a large data URL.
function canvasToBlob(canvas, type = 'image/png', quality) {
return new Promise((resolve, reject) => {
canvas.toBlob(blob => {
if (blob) resolve(blob);
else reject(new Error('Canvas could not be encoded'));
}, type, quality);
});
}
const canvas = document.querySelector('#canvas');
const blob = await canvasToBlob(canvas, 'image/webp', 0.85);
const temporaryUrl = URL.createObjectURL(blob);
document.querySelector('#preview').src = temporaryUrl;
// Call URL.revokeObjectURL(temporaryUrl) when the preview is no longer needed.
Use the fetch-and-FileReader path when preserving the downloaded bytes matters. Use canvas when you intentionally want a rendered export.
Converting a file selected by the user
A local File obtained from an <input type='file'> is a Blob subtype, so the same FileReader method works without a URL request:
const input = document.querySelector('#file-input');
input.addEventListener('change', () => {
const file = input.files[0];
if (!file) return;
const reader = new FileReader();
reader.addEventListener('load', () => {
const dataUrl = reader.result;
document.querySelector('#preview').src = dataUrl;
}, { once: true });
reader.addEventListener('error', () => {
console.error(reader.error);
}, { once: true });
reader.readAsDataURL(file);
});
This produces the same kind of complete data URL. If an upload service asks for raw Base64, pass the result through dataUrlToBase64() after the read completes.
Which method should you choose?
| Need | Use | Important consideration |
|---|---|---|
| Preserve bytes fetched from an image URL | fetch() → blob() → readAsDataURL() |
Cross-origin requests require server CORS permission; output is a complete data URL. |
| Convert a local file input | FileReader.readAsDataURL(file) |
A selected File is already a Blob; remove the declaration only when required. |
| Export an image already loaded or modified in the page | Canvas → toDataURL() |
Remote pixels need CORS approval; canvas export may change the image. |
| Handle large rendered output | Canvas → toBlob() |
A Blob and object URL avoid assembling a large data URL string in memory. |
Reliability, memory, and security considerations
Check status before reading bytes
fetch() resolves for HTTP error responses, so always test response.ok. Without that check, a 404 page or an access-denied response may be encoded as if it were an image.
Validate what you received
For uploads or API forwarding, inspect blob.type and, where appropriate, enforce an allow-list of image MIME types. A URL ending in .jpg is not proof that the response is a JPEG. Also apply an application-specific size limit before converting or storing data.
Remember that data URLs expose their contents
Anyone who can read the resulting string can read the image. Do not place private images in a data URL embedded in public HTML, logs, analytics events, or URLs. Base64 is an encoding, not encryption.
Avoid unnecessary conversions
If the destination accepts a Blob, ArrayBuffer, or multipart upload, sending the original Blob is usually simpler than creating a data URL and then stripping its prefix. Convert only when the receiving interface specifically requires Base64 or a data URL.
Common errors and fixes
“Failed to fetch” or a CORS error
The browser could not read the cross-origin response. Configure CORS on the image host, use an endpoint under your control, or perform the request on a server. A successful display in an <img> tag does not change this requirement.
The function returns an HTML document
The URL may redirect to a sign-in page, anti-bot challenge, or error document. Inspect the status, final URL, response headers, and blob.type before treating the result as an image.
Rank #4
Canvas throws SecurityError
The canvas is tainted by pixels from a source that did not grant CORS access. Set crossOrigin before src and obtain the correct response headers; otherwise canvas export is intentionally blocked.
The Base64 string is rejected by an API
Check whether the API wants a complete data URL or only the payload. Use dataUrl for the former and dataUrlToBase64(dataUrl) for the latter. Also verify that the MIME type and the API’s size limits match the image you sent.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →The page becomes slow or runs out of memory
Large data URLs require a large in-memory string. Prefer Blob-based uploads or canvas toBlob(), release object URLs with URL.revokeObjectURL(), and avoid keeping duplicate strings in state, logs, and DOM attributes.
Or skip the browser setup
If what you really need is a clean screenshot of a webpage before converting or embedding the resulting image, ScreenshotNeo returns a PNG, JPEG, WebP, or PDF from one GET request. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Only clean shots are billed, while bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the outcome with X-Page-Verdict and X-Billed headers.
Use the API details and options in the ScreenshotNeo documentation. A cURL request 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 same request in JavaScript is:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const screenshotBlob = await res.blob();
const screenshotDataUrl = await new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = () => reject(reader.error);
reader.readAsDataURL(screenshotBlob);
});
Python and Node.js callers can save the response directly:
Recommended Free Tools
import requests
r = requests.get('https://api.screenshotneo.com/v1/shot', params={'access_key': 'YOUR_API_KEY', 'url': 'https://stripe.com'}, timeout=90)
r.raise_for_status()
open('shot.webp', 'wb').write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const bytes = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', bytes));
ScreenshotNeo also provides an MCP server for Claude, Cursor, and other MCP clients, plus full-page capture with lazy images loaded, CSS-selector element capture, device presets, retina scale, PDF controls, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, timezone and geolocation, transparent backgrounds, resizing, cache TTLs, signed links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage data, and an OpenAPI specification. Every feature is included on every plan. The free plan includes 1,000 shots each month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
Best Value
Server-side JavaScript is a separate case
The code above relies on browser Web APIs and specifically addresses browser CORS behavior. A Node.js process does not automatically follow the same browser security model, but its HTTP, Blob, and file APIs vary by Node version and project setup. Choose an implementation for the exact runtime and test its response handling, MIME validation, memory use, and redirect policy rather than copying a browser recipe unchanged.
Frequently Asked Questions
Does Base64 reduce an image’s file size?
No. Base64 is a text encoding and is generally less compact than the original binary bytes. Use it for compatibility with an interface that requires text, not as a compression method.
Can I put a Base64 image in CSS?
Yes, when the CSS property accepts a URL, a complete data URL can be used with syntax such as background-image: url('data:image/png;base64,...'). Keep the MIME declaration and consider the memory and caching consequences for large images.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Why does my data URL contain a different MIME type than the file extension?
The MIME declaration comes from the response Blob or the canvas export type, not from the URL’s filename. Check the server response and request the output format you actually need.
Is a data URL permanent?
The string remains usable as long as you retain it, but it is not a hosted backup. Store the original bytes or an image URL when you need durable storage, access control, or efficient caching.
Quick Recap
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.




