ScreenshotNeo

BlogHow-to

How to Increase Image Resolution in Html2canvas Downloads

Increase html2canvas download quality with scale, correct viewport sizing, CORS fixes, export code, troubleshooting, and a browser-free ScreenshotNeo option.

By the ScreenshotNeo team30 September 20268 min read

How to Increase Image Resolution in Html2canvas Downloads

Use the scale option. It controls the rendering scale used by html2canvas and defaults to window.devicePixelRatio. A larger value creates a canvas with more pixels, which usually produces a sharper download when the page is rendered at the same CSS size. Export the returned canvas with toDataURL() or toBlob().

const element = document.querySelector('#capture');
const scale = 2;

const canvas = await html2canvas(element, { scale });
const link = document.createElement('a');
link.download = 'capture.png';
link.href = canvas.toDataURL('image/png');
link.click();

The important distinction is that scale increases rendered pixel dimensions; it cannot create detail that is absent from a low-resolution source image. If a source photo is only 300 pixels wide, rendering it at scale 4 does not recover detail beyond those pixels.

What html2canvas resolution means

html2canvas reconstructs a screenshot from the DOM, styles and loaded resources in the browser. It does not copy the browser’s already-composited framebuffer. Unsupported or partially supported CSS can therefore make the result differ from what you see on screen. The official documentation describes this rendering model.

Suppose the captured element is 800 CSS pixels wide and 600 CSS pixels high:

Scale Approximate canvas size Pixel area Typical trade-off
1 800 × 600 480,000 pixels Smallest memory and fastest rendering
2 1,600 × 1,200 1,920,000 pixels Sharper output, more memory
3 2,400 × 1,800 4,320,000 pixels Higher detail, slower and larger output

Those dimensions are arithmetic based on the CSS dimensions and chosen scale, not a universal quality benchmark. Doubling scale multiplies pixel area by four because both axes double.

Complete browser example

Save this as an HTML file, open it in a modern browser, and click the button. It captures a card at scale 2 and downloads a PNG.

The scale option increases the canvas pixel dimensions while the CSS layout remains the same.
The scale option increases the canvas pixel dimensions while the CSS layout remains the same.
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>High-resolution html2canvas download</title>
  <script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
  <style>
    body { font-family: system-ui, sans-serif; margin: 2rem; }
    #capture { width: 800px; padding: 2rem; background: white; color: #172033; }
  </style>
</head>
<body>
  <section id="capture">
    <h1>Quarterly report</h1>
    <p>This element is rendered at a larger pixel scale before export.</p>
  </section>
  <button id="download">Download PNG</button>
  <script>
    document.querySelector('#download').addEventListener('click', async () => {
      const element = document.querySelector('#capture');
      const scale = 2;
      const canvas = await html2canvas(element, {
        scale,
        backgroundColor: '#ffffff',
        logging: false
      });

      const link = document.createElement('a');
      link.download = 'quarterly-report.png';
      link.href = canvas.toDataURL('image/png');
      link.click();
    });
  </script>
</body>
</html>

Inspect the result rather than assuming a setting worked:

console.log(canvas.width, canvas.height);

The canvas width and height should be close to the element’s rendered CSS dimensions multiplied by scale. Browser zoom and device pixel ratio can affect what you observe, so measure the actual canvas.

Choose scale without breaking the browser

Start with an explicit value such as 2, then check output dimensions, memory use and rendering time in the browsers your users actually run. A very high value can exceed browser canvas limits. The official FAQ explains that limits vary by browser and platform; an over-limit canvas may be blank or only partially rendered. There is no durable, universal maximum you can safely promise.

Keep the CSS layout and pixel density separate

  • scale controls pixel density.
  • windowWidth and windowHeight control the rendering context and can change responsive breakpoints.
  • CSS width and height change the element’s layout, rather than simply making the same layout denser.

For a long element that is clipped, use its scroll dimensions as the rendering window:

const element = document.querySelector('#capture');
const canvas = await html2canvas(element, {
  scale: 2,
  windowWidth: element.scrollWidth,
  windowHeight: element.scrollHeight
});

This addresses capture extent. It does not bypass canvas memory limits, and it may produce a very large bitmap for long pages. For especially long documents, capture sections separately and assemble them in a format designed for pages.

Export PNG, JPEG or a Blob

The official examples use canvas.toDataURL('image/png'). PNG is lossless and preserves text and sharp edges, but can be large. JPEG is smaller for photographic content and introduces lossy compression. A Blob avoids keeping a long base64 string in memory and is convenient for uploads.

// PNG data URL
const pngUrl = canvas.toDataURL('image/png');

// JPEG with quality from 0 to 1
const jpegUrl = canvas.toDataURL('image/jpeg', 0.9);

// Blob download
canvas.toBlob((blob) => {
  if (!blob) throw new Error('Canvas export failed');
  const url = URL.createObjectURL(blob);
  const link = document.createElement('a');
  link.download = 'capture.png';
  link.href = url;
  link.click();
  URL.revokeObjectURL(url);
}, 'image/png');

Load images correctly: CORS and timing

External images are a frequent reason for missing content or a failed export. For cross-origin images, html2canvas can use useCORS: true when the image server supplies the appropriate CORS headers. Otherwise, use a proxy under your control. The official examples and FAQ cover this requirement.

const canvas = await html2canvas(element, {
  scale: 2,
  useCORS: true,
  imageTimeout: 15000
});

useCORS is not a permission bypass. The remote server must allow the browser origin. Set imageTimeout to avoid waiting indefinitely for a resource, then verify that every important image has loaded before calling html2canvas.

await Promise.all(
  [...document.images].map((img) => {
    if (img.complete) return Promise.resolve();
    return new Promise((resolve) => {
      img.addEventListener('load', resolve, { once: true });
      img.addEventListener('error', resolve, { once: true });
    });
  })
);
const canvas = await html2canvas(element, { scale: 2, useCORS: true });

Useful html2canvas options

Option Use Resolution implication
scale Rendering density; defaults to window.devicePixelRatio Primary control for more output pixels
windowWidth, windowHeight Virtual viewport dimensions Prevents clipping and controls responsive layout
useCORS Requests cross-origin images with CORS Prevents missing or tainted image content when headers permit it
backgroundColor Sets the canvas background Does not increase detail; use null for transparency where supported
imageTimeout Maximum image-loading wait Controls reliability and wait time, not pixel density
logging Diagnostic console output Useful while debugging resource and CSS problems
onclone Adjusts the cloned document before rendering Useful for hiding controls or applying capture-only styles

Capture-only styling and hidden content

Use onclone to make changes in html2canvas’s cloned document without altering the live page. This is useful for removing a sticky toolbar, expanding a collapsed panel, or setting a predictable background.

const canvas = await html2canvas(element, {
  scale: 2,
  onclone: (clonedDocument) => {
    clonedDocument.querySelectorAll('.no-print').forEach((node) => {
      node.style.display = 'none';
    });
    clonedDocument.body.style.background = '#ffffff';
  }
});

Make sure fonts have loaded before capture. A fallback font can change line wrapping and make the output appear different even when the scale is correct.

if (document.fonts?.ready) {
  await document.fonts.ready;
}

Performance, memory and reliability checklist

  • Measure canvas.width and canvas.height after rendering.
  • Increase scale gradually. Pixel area grows with the square of scale.
  • Capture only the needed element instead of the entire document.
  • Split extremely long pages into sections when a single canvas becomes too large.
  • Prefer Blob export for large images so you do not create a large base64 string unnecessarily.
  • Wait for fonts, images and application data before capture.
  • Use logging: true while diagnosing missing resources, then disable it.
  • Test at the target viewport because media queries affect the reconstructed DOM.

html2canvas is client-side. Node.js by itself does not provide the window, document and computed-style APIs it needs. For server-side screenshots, use real-browser automation or a screenshot service.

Troubleshooting common failures

The image is still blurry

Confirm that scale is actually passed to the html2canvas call and inspect the resulting canvas dimensions. Check the source images: a low-resolution raster cannot gain missing detail. Also verify that browser zoom, CSS transforms and responsive layout are not shrinking the element before capture.

The output is blank or partially rendered

The canvas may have exceeded a browser or platform limit. Reduce scale, capture a smaller region, or split the page. The FAQ describes these limits as variable rather than a single guaranteed number.

Images are missing

Check whether the image is cross-origin. Add useCORS: true only when the server sends a suitable CORS header, or route the asset through a proxy. Also wait for image loading and check the browser console for CORS errors.

The capture is clipped

Use windowWidth: element.scrollWidth and windowHeight: element.scrollHeight for a full element. Check overflow containers and fixed-height parents. Increasing scale alone does not expand the layout.

Fonts or CSS look different

Wait for document.fonts.ready, capture after asynchronous data has rendered, and review whether the CSS property is supported by html2canvas. Because it reconstructs the DOM, unsupported CSS can differ from the live browser view.

It works locally but not in production

Compare origins, CSP rules, image headers, font URLs and authentication. Production often serves assets from a different domain, which exposes CORS and loading differences that a local setup does not.

Or skip the browser setup

If you need a server-ready screenshot rather than a DOM reconstruction in each user’s browser, ScreenshotNeo accepts one GET request and returns PNG, JPEG, WebP or PDF. See the ScreenshotNeo API documentation for all options.

A capture service can clean common overlays before returning the screenshot.
A capture service can clean common overlays before returning the screenshot.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
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 accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups and chat widgets; each step can be disabled. Bot checks, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and whether it was billed. Its MCP server lets Claude, Cursor and other MCP clients use take_screenshot, get_page_info and capture_pdf. The Free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 screenshots.

Create a free ScreenshotNeo account to get 1,000 screenshots each month with no card.

FAQ

Is scale 2 always the best value?

No. It is a practical starting point, but the right value depends on target dimensions, browser memory and the detail in your source assets.

Does scale change the downloaded file format?

No. Use the MIME type in toDataURL or toBlob to choose PNG, JPEG or another supported export format.

Can html2canvas create a native browser screenshot?

No. It rebuilds the image from DOM and CSS information. Use browser automation or a screenshot API when you need a server-side capture of a rendered page.

Why does changing width not fix pixelation?

Changing width changes layout. scale changes rendering density. They solve different problems and can be combined when you need both a different responsive layout and more pixels.

What should I check first when quality is poor?

Check the actual canvas dimensions, the chosen scale, source image resolution, loaded fonts and whether the element was resized by responsive CSS.