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.

Short answer: Do not assume that setting CSS letter-spacing is enough. html2canvas rebuilds text on a canvas rather than asking the browser to take a native screenshot, and its CSS support is implemented property by property. Verify the html2canvas version in your project, wait for fonts to finish loading, compare a small sample with the browser rendering, and inspect that version’s text-rendering code. The retrieved master renderer has a nonzero-spacing path that measures graphemes but, in the shown reducer, does not add the spacing value to each advance. That finding may explain a mismatch, but it must be confirmed against the exact release you use.

Why letter spacing changes in html2canvas

html2canvas reconstructs an image from the DOM and CSS values. It is not a pixel capture of the browser’s already-painted page. The project’s FAQ explains the consequence: “Every CSS property must be manually implemented to render correctly, so html2canvas will never have full CSS support.” A browser screenshot and an html2canvas canvas can therefore differ even when the page looks correct in Chrome, Firefox or Safari.

The feature list includes letter-spacing, which means the property is recognized as a supported feature. It does not prove precise output for every value, font, browser or html2canvas release. In the surfaced master CanvasRenderer, nonzero spacing is handled by renderTextWithLetterSpacing and segmentGraphemes. The shown reducer advances by each grapheme’s measured width, but does not visibly add the letterSpacing argument to that advance. Treat this as a source observation about that master snapshot, not a statement about every published package.

MDN’s CanvasRenderingContext2D.letterSpacing property also does not settle the issue: the retrieved html2canvas implementation uses its own helper, so the existence of a browser canvas API does not establish that html2canvas calls it.

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

Build a reproducible test before changing your page

Record the variables that can alter the result. Without them, a spacing complaint is difficult to distinguish from a font-loading or text-segmentation problem.

  • The exact installed html2canvas version (including release candidate or commit).
  • Browser and operating-system versions.
  • The computed letter-spacing value and its sign: zero, positive or negative.
  • The computed font family, weight, size and text direction.
  • Whether the font is a system font, a local file or a web font that loads dynamically.
  • The text sample and the browser rendering used as the visual reference.

Use a short string containing repeated letters, punctuation and, if relevant, emoji or combining marks. Capture it three times: with letter-spacing: 0, a positive value such as 2px, and a negative value such as -1px. Compare the DOM and the resulting canvas at the same displayed scale. This isolates spacing from layout, wrapping and image-scaling effects.

Minimal working example

Keep the text in a dedicated element and make the font explicit. The example below lets you test spacing without the rest of an application affecting the result.

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
  <style>
    #sample {
      display: inline-block;
      font-family: Arial, sans-serif;
      font-size: 32px;
      font-weight: 400;
      letter-spacing: 2px;
      white-space: nowrap;
    }
  </style>
</head>
<body>
  <div id="sample">Letter spacing 123</div>
  <button id="capture">Capture</button>
  <script>
    document.getElementById('capture').addEventListener('click', async () => {
      const node = document.getElementById('sample');
      const canvas = await html2canvas(node, { backgroundColor: '#fff' });
      document.body.appendChild(canvas);
    });
  </script>
</body>
</html>

The CDN URL above is only an example. In an application, pin the version through your package manager and record the actual version in the diagnostic output. Do not diagnose a production issue from a different release.

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.

Make sure the font is ready before capture

A custom font can change glyph widths after the first layout. If html2canvas captures before the font is available, the browser may initially lay out text with a fallback face and later repaint it with the web font. A historical issue report described changed spacing with a dynamically loaded font in html2canvas 1.0.0-rc.5, Chrome 80 and Ubuntu. That is an individual report, not proof of a current universal bug, but it is a useful reproduction variable.

Rank #2
Sale
HTML and CSS: Design and Build Websites
  • HTML CSS Design and Build Web Sites
  • Comes with secure packaging
  • It can be a gift option

Wait for the document’s font promises before calling html2canvas:

await document.fonts.ready;
const node = document.querySelector('#sample');
const canvas = await html2canvas(node);

For a specific face, request it explicitly and verify that it is loaded:

await document.fonts.load('400 32px "My Web Font"');
if (!document.fonts.check('400 32px "My Web Font"')) {
  throw new Error('The required font is not available');
}
const canvas = await html2canvas(document.querySelector('#sample'));

Also ensure that the @font-face declaration, weight and style exactly match the computed style. A browser can silently synthesize a weight or select a fallback family, producing a legitimate width difference that looks like a letter-spacing defect.

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

Inspect the computed value and output

Before capture, log the values that html2canvas receives through the cloned DOM:

const node = document.querySelector('#sample');
const style = getComputedStyle(node);
console.table({
  letterSpacing: style.letterSpacing,
  fontFamily: style.fontFamily,
  fontSize: style.fontSize,
  fontWeight: style.fontWeight,
  direction: style.direction,
  whiteSpace: style.whiteSpace
});

Check child spans as well. A parent can have one spacing value while an inline child overrides it. CSS inheritance, transforms, fractional font sizes and a device-pixel ratio can make a correct spacing value appear different when the canvas is displayed at a resized CSS width.

Use onclone for controlled experiments

html2canvas documents an onclone callback that runs after it clones the document and before rendering. Changes made there affect the clone used for the capture, not the original page. This makes it suitable for testing hypotheses without flashing altered typography to users.

const canvas = await html2canvas(document.querySelector('#sample'), {
  onclone: (clonedDocument) => {
    const clone = clonedDocument.querySelector('#sample');
    clone.style.letterSpacing = '2px';
  }
});

This callback is an experiment point, not a guaranteed typography fix. If the renderer path does not apply nonzero spacing, assigning the same CSS value in the clone may produce the same output. Validate the canvas visually and retain the smallest reproduction when reporting the result.

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

You can also temporarily remove variables in the clone:

const canvas = await html2canvas(node, {
  onclone: (clonedDocument) => {
    const clone = clonedDocument.querySelector('#sample');
    clone.style.fontFamily = 'Arial, sans-serif';
    clone.style.letterSpacing = '0px';
    clone.style.whiteSpace = 'nowrap';
  }
});

If the zero-spacing capture matches but positive or negative values do not, spacing handling is a stronger suspect. If all values differ, investigate the font, scale, dimensions or other CSS first.

Check the renderer in the version you actually ship

Look at the installed package or its tagged source rather than assuming the current master represents your build. In the retrieved master source, the nonzero branch segments graphemes, measures each one and advances the drawing position. The displayed reducer does not add the spacing argument to that measured width. If your installed source has the same logic, CSS alone cannot be expected to reproduce every nonzero value.

Rank #4
Sale
Web Design with HTML, CSS, JavaScript and jQuery Set
  • 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

Do not patch a dependency blindly. A local modification can be overwritten on reinstall and can interact with combining marks, right-to-left text, emoji sequences and negative spacing. If you must experiment, keep the change in a reproducible fork or patch file, add a visual regression test for zero, positive and negative spacing, and check the result after every dependency update.

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

Important edge cases

Negative letter spacing

Negative values are especially useful for exposing whether spacing is actually added between graphemes. A historical issue reported trouble with negative spacing. Reproduce it separately; do not infer that a positive-value result predicts the negative path.

Grapheme clusters

Modern text is not always one code point per visible character. Emoji sequences, combining accents and some scripts must remain together. html2canvas’s grapheme segmentation is intended to account for this, but any workaround that manually splits a string into characters can create incorrect output. Compare ordinary Latin text first, then add the characters that matter to your application.

Direction and shaping

Right-to-left text, ligatures and complex-script shaping introduce additional layout behavior. Record the element’s direction and language, and avoid judging a workaround from a Latin-only sample if your real content uses another script.

Transforms and scaling

A CSS transform, a non-default scale option or a high device-pixel ratio changes where edges land in the bitmap. Compare the canvas’s intrinsic width and height, not only its CSS display size. Preserve the intrinsic bitmap when measuring glyph positions.

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

Common symptoms, causes and fixes

Symptom Likely cause Action
Zero spacing matches; positive spacing collapses The renderer’s nonzero path may not add the spacing value. Inspect the exact installed renderer source and test a release known to behave correctly for your case; do not rely on the feature-list entry alone.
Spacing changes after a web font loads Capture occurred before the final font was available, or the requested weight is missing. Await document.fonts.ready, call document.fonts.load, and verify with document.fonts.check.
Only negative values fail A sign-specific spacing or overlap path differs. Reproduce with an isolated negative sample and record the version, browser and OS.
Letters look right but the whole image is offset Canvas scaling, transforms or a resized display bitmap. Compare intrinsic dimensions and remove transforms in the test case.
Only one inline phrase differs A child element overrides inherited font or spacing styles. Log computed styles for the element and every relevant child.
Patch works locally but not after deployment A different package version or browser is running in production. Log the deployed version and reproduce with the production browser matrix.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

When html2canvas is the wrong capture method

If typographic fidelity is non-negotiable, a browser-level screenshot captures the browser’s painted result rather than reimplementing CSS in a canvas renderer. That approach has its own setup, including a browser process, viewport control, font installation and waiting for network and font readiness. It is often the more predictable choice for exact visual regression images.

Or skip the browser setup

ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns PNG, JPEG, WebP or PDF, so the browser setup is handled for you. Before capture it accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets; each step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.

For a quick capture:

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

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}`);

See the ScreenshotNeo documentation for authentication and options. It supports full-page captures with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets plus custom viewports, retina scale, custom CSS and JavaScript, click and wait actions, network-idle waits, request blocking, cookies, headers, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, TTL caching, signed links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API and an OpenAPI specification. PDF controls include paper size, margins, landscape mode and page ranges. Parameter names used by other screenshot APIs also work, which can simplify migration.

An MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients, allowing AI agents to capture pages without custom browser orchestration. Pricing is Free: 1,000 shots per month with no card; Starter: $5 for 3,000; Growth: $15 for 15,000; Pro: $39 for 60,000; Scale: $99 for 250,000; and Business: $249 for 1,000,000. Yearly billing gives two months free, and every feature is included on every plan. Create a free ScreenshotNeo account to start with 1,000 screenshots a month and no card.

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

A practical decision path

  1. Confirm the installed html2canvas version and capture environment.
  2. Wait for fonts, then log computed spacing and font properties.
  3. Test zero, positive and negative values on a short sample.
  4. Compare the browser DOM with the canvas at intrinsic resolution.
  5. Inspect the release’s renderer source if nonzero spacing alone fails.
  6. Use onclone only for isolated, validated experiments.
  7. If exact browser typography is required, use a browser screenshot service or controlled browser capture instead of relying on a canvas reimplementation.

Frequently Asked Questions

Does html2canvas officially support letter-spacing?

Its feature list includes letter-spacing, but that listing does not guarantee identical output for every value, font, browser or release. Verify the renderer in the version you install.

Will waiting for document.fonts.ready always fix spacing?

No. It resolves a common dynamic-font variable. If the renderer does not apply the nonzero spacing value, font readiness alone cannot correct the output.

Can I change the original element in onclone?

onclone changes the cloned document used for rendering, leaving the original page untouched. Treat any typography adjustment as an experiment and validate the resulting canvas.

Should I manually insert spaces between characters?

Generally no. Manual splitting can break grapheme clusters, combining marks, emoji sequences and complex scripts. Diagnose the renderer and font first.

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

The Bottom Line

Preserving letter spacing with html2canvas requires verification, not a single CSS declaration: pin and inspect the version, wait for the final font, test all spacing signs, and compare intrinsic canvas output with the browser. If the renderer omits the spacing advance in your release, use a validated patch or a browser-level screenshot method instead.

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.