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.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Use JavaScript and html2canvas when the HTML is already open in a browser; use a headless browser such as Browsershot (Puppeteer plus Chrome) when PHP must render the page on a server. PHP’s imagejpeg() only encodes an existing GD image—it does not lay out HTML or execute JavaScript.

The right method depends on where the capture runs, how closely it must match browser pixels, whether page scripts must execute, and whether a person downloads the file or your server stores it.

Choose the rendering path first

Need Best fit Important limitation
A user clicks Download in an already rendered page Browser JavaScript with html2canvas and Canvas JPEG export It reconstructs the page from DOM data, so unsupported CSS or cross-origin assets can differ from the visible browser.
PHP must render a URL or HTML without a user’s browser Spatie Browsershot, which drives Puppeteer and headless Chrome Requires a compatible Node, Puppeteer, Chrome, fonts, permissions and network setup.
You already have a GD image PHP imagejpeg() It is an encoder, not an HTML renderer.
You need a PDF document rather than browser-like JPEG pixels dompdf or another PDF renderer dompdf’s JavaScript setting does not execute JavaScript like a web browser.

Generate a JPEG in browser JavaScript

1. Load html2canvas

Install or serve the html2canvas build used by your project, then call it with the exact element to capture. Capturing an element rather than the whole document reduces memory use and avoids browser controls.

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

2. Wait for content that affects the image

Fonts, images and application data may load after the initial DOM exists. Start the capture only after those resources are ready. If a particular image is important, wait for its load event; for web fonts, wait for document.fonts.ready where supported.

3. Capture and download a JPEG

async function downloadJpeg(element) {
  if (!element) throw new Error('Capture element was not found');

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

  const images = Array.from(element.querySelectorAll('img'));
  await Promise.all(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: window.devicePixelRatio || 1,
    backgroundColor: '#ffffff'
  });

  canvas.toBlob((blob) => {
    if (!blob) {
      console.error('JPEG export failed');
      return;
    }
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = 'capture.jpg';
    document.body.appendChild(link);
    link.click();
    link.remove();
    URL.revokeObjectURL(url);
  }, 'image/jpeg', 0.92);
}

downloadJpeg(document.querySelector('#capture'))
  .catch(error => console.error(error));

The third argument to toBlob() is JPEG quality from 0 to 1. A value such as 0.92 is a practical starting point; lower values generally make smaller files with more compression artifacts. toBlob() avoids constructing a large base64 string in JavaScript memory.

Crop, hide controls and choose resolution

Pass x, y, width and height to html2canvas when a crop is required. Add data-html2canvas-ignore to buttons, toolbars or other interface elements that should not appear. Set an explicit scale when you need predictable output dimensions: a higher scale is sharper but consumes substantially more memory and produces a larger file. Device-pixel-ratio scaling is useful for high-density displays, but it is not a promise of unlimited resolution.

const canvas = await html2canvas(document.querySelector('#invoice'), {
  x: 0,
  y: 0,
  width: 900,
  height: 1200,
  scale: 2,
  backgroundColor: '#fff'
});

JPEG’s transparency rule

JPEG has no alpha channel. Transparent regions become an implementation-defined or supplied background, so set backgroundColor deliberately. If transparency is a requirement, export PNG or WebP instead.

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

What html2canvas can and cannot reproduce

html2canvas does not take a literal operating-system screenshot. It reads DOM elements and CSS properties it understands and paints its own canvas representation. The result can differ from the pixels a browser displayed, especially with unsupported CSS, complex effects, filters, video, plugins or unusual fonts. Check the project’s supported-features documentation and test representative pages.

Cross-origin images and canvas security

An image served from another origin can taint the canvas. Once tainted, browser security rules prevent reading or exporting it. html2canvas documents a proxy option, but that proxy must be under your control and must return resources with suitable cross-origin headers; the library cannot bypass browser security policy. A same-origin copy of the asset is often the simplest fix.

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

Iframes

Same-origin iframes can be traversed recursively. Cross-origin frames cannot be read because the browser blocks access to their document. A sandboxed iframe without allow-same-origin has the same practical restriction.

External fonts and late layout changes

If a font swaps after capture, text wrapping and element heights can change. Wait for fonts and images, freeze animations where possible, and capture only after the application has reached a stable state.

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

Render HTML to JPEG in PHP with Browsershot

When the server must create the image, use a real browser engine. Spatie Browsershot accepts a URL or arbitrary HTML and controls Puppeteer, which controls headless Chrome. That allows page JavaScript and browser layout to run before the image is saved.

<?php

require __DIR__ . '/vendor/autoload.php';

use SpatieBrowsershotBrowsershot;

Browsershot::html('<!doctype html>
<html>
  <body style="font-family: sans-serif; padding: 32px">
    <h1>Hello world</h1>
    <p>Generated by a headless browser.</p>
  </body>
</html>')
    ->save(__DIR__ . '/capture.jpg');

Before deploying, verify the Browsershot version’s installation instructions and image options, plus the Node, Puppeteer and Chrome versions available to your host. Confirm the executable path, filesystem permissions, installed fonts, outbound network access and any sandbox settings. The API shown above establishes the HTML-to-image flow; exact image-format controls can vary by version, so use the documentation for the release you install.

When Browsershot is the better choice

  • The HTML depends on JavaScript to build its final content.
  • You need browser layout rather than a DOM approximation.
  • A scheduled job, queue worker or web request must create and store the JPEG without a user’s screen.
  • You need to render a URL, including its loaded styles and scripts, in a controlled environment.

Operational costs and safeguards

Launching Chrome is heavier than creating a canvas in an existing tab. Reuse workers where your deployment supports it, cap concurrent captures, and set request and navigation timeouts. Restrict untrusted HTML and URLs: a renderer can access network resources, consume CPU and memory, or expose internal services if your design allows arbitrary destinations. Keep temporary files isolated and remove them after delivery.

Why PHP imagejpeg() is not an HTML converter

The PHP manual defines imagejpeg() as creating a JPEG file from a supplied image. It accepts a GD GdImage, writes to a path or stream, and can send image bytes directly when no filename is supplied. Set the response content type before direct output.

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

$image = imagecreatetruecolor(800, 450);
$white = imagecolorallocate($image, 255, 255, 255);
$blue = imagecolorallocate($image, 30, 90, 180);
imagefill($image, 0, 0, $white);
imagefilledrectangle($image, 40, 40, 760, 410, $blue);

header('Content-Type: image/jpeg');
imagejpeg($image, null, 85);
imagedestroy($image);

The quality argument ranges from 0 (lower quality and usually smaller output) to 100 (higher quality and usually larger output). If omitted, PHP uses the IJG default, approximately 75. This code draws a GD image; it does not parse HTML, apply CSS or run JavaScript. Render HTML first with a browser engine, then use GD only if you need additional raster processing.

Why dompdf is usually the wrong JPEG tool

dompdf is an HTML-to-PDF renderer. Its documentation explicitly notes that enabling its JavaScript option does not make it parse JavaScript like a web browser. It may suit a PDF workflow whose supported HTML and CSS are sufficient, but it is not a drop-in browser-screenshot engine for arbitrary, JavaScript-heavy pages. If the required artifact is a browser-rendered JPEG, choose a headless browser instead.

Browser capture versus server rendering

Decision axis html2canvas Browsershot
Where it runs End user’s browser Your PHP server and headless Chrome
Visual model DOM/CSS reconstruction Actual browser layout and paint pipeline
Page JavaScript Already running in the page Executed by the controlled browser
Cross-origin access Constrained by browser security and canvas tainting Controlled by server network policy and browser configuration
Infrastructure Little beyond the web page Node, Puppeteer, Chrome, fonts, permissions and process management
Typical delivery Immediate user download Stored file, API response, queue job or batch output

Common failures and fixes

“JPEG export failed” or a null Blob

The browser could not serialize the canvas, or the canvas dimensions exceeded available memory. Capture a smaller element, lower scale, reduce crop dimensions and test another supported browser.

SecurityError when calling toBlob or reading pixels

A cross-origin resource probably tainted the canvas. Serve the asset from the same origin, configure appropriate CORS headers on the asset and request it with the required credentials mode, or use a carefully designed proxy.

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.
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

Missing images, fonts or dynamic content

Capture began before asynchronous work completed. Wait for image loads, document.fonts.ready, application-specific readiness signals and any data-fetching promises. Disable animations that move content during capture.

The output looks different from the page

html2canvas supports a defined subset of CSS and reconstructs rather than photographs. Simplify unsupported effects, test the project’s supported-features list, or move the job to Browsershot for browser-engine rendering.

Browsershot cannot launch Chrome

Check Node and Puppeteer installation, the Chrome executable path, process permissions, sandbox configuration, installed fonts and outbound access. Run the same command as the production user and inspect the captured process error.

PHP output is corrupt or contains warnings

For direct GD output, send Content-Type: image/jpeg and ensure no whitespace, notices or debug text is emitted before the binary bytes. Prefer writing to a file when another middleware layer may modify the response.

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

The PDF route ignores JavaScript

That is expected for dompdf’s JavaScript setting. Use a browser-driven renderer when scripts must execute, or redesign the document so the required content is present in server-rendered HTML.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance, reliability and cost considerations

  • Limit pixels: memory grows with canvas width, height and scale. Capture the required region instead of an entire long page when possible.
  • Control JPEG quality: choose a quality appropriate to text, photos and storage limits, then inspect artifacts at the final display size.
  • Stabilize inputs: use fixed viewport dimensions, deterministic fonts, frozen animation state and explicit backgrounds.
  • Handle retries: distinguish a missing asset, a timeout, a browser crash and a canvas security error; retrying a deterministic CORS error will not fix it.
  • Protect servers: queue expensive browser jobs, cap concurrency and enforce navigation and total-job timeouts.
  • Validate outputs: check that a file exists, has nonzero length and begins with a valid JPEG signature before publishing it.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP or PDF, so you do not have to install Chrome, Puppeteer or PHP browser bindings. Before capture it accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing result.

It also provides full-page and CSS-selector captures, dark mode, device presets and custom viewports, retina scale, custom CSS and JavaScript, click and wait actions, request blocking, headers, cookies, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, configurable-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. An MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients.

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

See the ScreenshotNeo documentation for parameters and response behavior. The same endpoint works from Python:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
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)

And from 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}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is on every plan, and yearly billing gives two months free. Create a free ScreenshotNeo account to try the endpoint.

Practical decision checklist

  1. Capture in JavaScript when a person already has the rendered page and a DOM approximation is acceptable.
  2. Use Browsershot when PHP must execute page JavaScript and produce browser-like output on the server.
  3. Use imagejpeg() only after you already have a GD image.
  4. Use dompdf for suitable PDF documents, not as a promise of browser JavaScript execution or arbitrary HTML-to-JPEG conversion.
  5. For a hosted API, choose ScreenshotNeo when removing consent UI, avoiding charges for failed pages, or connecting an AI agent matters.

Frequently Asked Questions

Can I convert an HTML string to JPEG without opening a browser?

Not with PHP’s GD encoder alone. Use a browser renderer such as Browsershot for HTML that needs layout or JavaScript, then save the resulting image.

Why is my canvas JPEG blurry?

Increase the capture scale deliberately, ensure the source element has the intended dimensions, and avoid enlarging a small JPEG after export. Higher scale increases memory use and file size.

Should I use PNG instead of JPEG?

Use PNG or WebP when you need transparency or lossless text and graphics. JPEG is useful when a smaller photographic file and no alpha channel are acceptable.

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

Is html2canvas a pixel-perfect screenshot tool?

No. It reconstructs a representation from DOM and CSS information. Unsupported features, cross-origin resources and font timing can make it differ from the browser’s displayed pixels.

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.