Recommended Free Tools
Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Visual Studio Code does not have a built-in command that exports any HTML file to PNG. The reliable workflow is to use VS Code to write and run browser-automation code, usually with Playwright: open the page in Chromium, wait for the content you need, then call page.screenshot() with a .png path. You can capture the visible viewport, the complete scrollable page, or one element.
This guide shows the complete setup, runnable JavaScript examples, local-file and development-server options, dynamic-page waits, Puppeteer alternatives, troubleshooting, and a hosted option when you do not want to maintain a browser installation.
What VS Code does—and what actually creates the PNG
VS Code is the editor and launch point for the script. The Playwright library launches a browser, renders the HTML, and writes the image. Microsoft’s VS Code guide documents Playwright integration for installing, editing, debugging, and generating tests; it does not document a VS Code command that converts arbitrary HTML directly.
Playwright’s Page API saves the image to the path you provide. A filename ending in .png produces PNG output. The same API can capture a viewport, a full page, or a locator (a selected element). See the Playwright Page API and the Playwright VS Code guide.
#1 Best Overall
Prerequisites and project setup
- Install a current Node.js release and Visual Studio Code.
- Use a project folder containing the HTML page or the application that serves it.
- Install the official Playwright extension from the VS Code Extensions view if you want the Test Explorer, debugging, and test-generation workflow.
Install Playwright through VS Code
- Open the project folder in VS Code.
- Open the Command Palette (
Ctrl+Shift+Pon Windows/Linux orCmd+Shift+Pon macOS). - Run Test: Install Playwright.
- Follow the setup prompts to install Playwright and its browser binaries. The exact prompts can vary with the extension and project type.
You can also initialize a Node project in the integrated terminal:
npm init -y
npm install -D playwright
npx playwright install chromium
The last command installs Chromium for Playwright. If your organization manages browsers centrally, use the browser and executable configuration approved for that environment instead.
Convert a page served by your local project
Serving the page through your development server is usually the least surprising method: relative CSS, JavaScript modules, fonts, and images resolve as they do in a normal browser. Start the server using your framework’s command, then point Playwright at its URL.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Minimal viewport screenshot
Create capture.js in the project root:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage({
viewport: { width: 1280, height: 800 }
});
await page.goto('http://localhost:3000', {
waitUntil: 'networkidle'
});
await page.screenshot({ path: 'screenshot.png' });
await browser.close();
})();
Run it in the integrated terminal with:
node capture.js
The file screenshot.png is written relative to the directory from which you run the command. Change the URL to your project’s actual address and make sure the server is running first.
Capture the complete scrollable page
Use fullPage: true when the output should include content below the viewport:
await page.screenshot({
path: 'full-page.png',
fullPage: true
});
Full-page capture uses the page’s rendered layout, including the viewport width you selected. Very long pages can produce a tall bitmap and consume more memory than a viewport shot.
Capture one element
Use a locator when you need a card, header, chart, or other component rather than the entire document:
Rank #2
- HTML CSS Design and Build Web Sites
- Comes with secure packaging
- It can be a gift option
await page.locator('.header').screenshot({
path: 'header.png'
});
Replace .header with a selector that exists on the page. If the selector matches multiple elements, narrow it with a more specific CSS selector or a locator filter.
Opening a standalone HTML file
A local file can be opened with a file:// URL, but asset paths and browser security rules can make this less predictable than using a local HTTP server. Build an absolute file URL with Node’s path utilities:
const path = require('node:path');
const { pathToFileURL } = require('node:url');
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1280, height: 800 } });
const htmlUrl = pathToFileURL(path.resolve('index.html')).href;
await page.goto(htmlUrl, { waitUntil: 'load' });
await page.screenshot({ path: 'index.png', fullPage: true });
await browser.close();
})();
If images, stylesheets, web fonts, modules, or fetch requests fail from file://, run a static server instead. For example, use the server command supplied by your framework or another local HTTP server, then navigate to its http://localhost address.
Make dynamic pages render before capture
A screenshot taken immediately after navigation can miss web fonts, lazy images, client-rendered data, animations, or content that appears after an interaction. Choose a wait that represents the page’s real ready state rather than relying on an arbitrary delay.
Wait for a meaningful selector
await page.goto('http://localhost:3000', { waitUntil: 'domcontentloaded' });
await page.locator('[data-page-ready="true"]').waitFor();
await page.screenshot({ path: 'ready.png', fullPage: true });
Add the data-page-ready attribute in your application after the required data and layout are ready. A selector wait is generally more maintainable than guessing how many milliseconds a page needs.
Wait for fonts and images
await page.goto('http://localhost:3000', { waitUntil: 'networkidle' });
await page.evaluate(async () => {
if (document.fonts) await document.fonts.ready;
await Promise.all(
Array.from(document.images)
.filter(img => !img.complete)
.map(img => new Promise(resolve => {
img.addEventListener('load', resolve, { once: true });
img.addEventListener('error', resolve, { once: true });
}))
);
});
await page.screenshot({ path: 'assets-loaded.png', fullPage: true });
Some applications keep analytics, sockets, or polling requests open indefinitely, so networkidle may never be reached. In that case, use domcontentloaded or load and then wait for a specific application-ready selector.
Disable motion for stable output
await page.addStyleTag({
content: `*, *::before, *::after {
animation: none !important;
transition: none !important;
caret-color: transparent !important;
}`
});
Stable rendering matters when you compare images across runs. Playwright’s visual-comparison guidance explains why operating systems, browser versions, settings, hardware, and headless mode can change pixels; keep those variables consistent when image differences matter. See Playwright visual comparisons.
Rank #3
Control dimensions, scale, and appearance
Viewport size
The viewport controls responsive breakpoints and the bitmap dimensions for a normal viewport screenshot:
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 →const page = await browser.newPage({
viewport: { width: 1440, height: 900 }
});
Choose dimensions that match the device or design state you are documenting. A different width can cause menus, columns, and typography to reflow.
Retina-style output
Increase the device scale factor when you need more pixels per CSS pixel:
const page = await browser.newPage({
viewport: { width: 1280, height: 800 },
deviceScaleFactor: 2
});
This changes the output pixel density and file size; it does not change the CSS layout width.
Dark mode and other media settings
await page.emulateMedia({ colorScheme: 'dark' });
Use 'light' or 'dark' according to the state you need. If the page uses a manual theme switch, click that control before capturing and wait for the resulting UI to settle.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Hide or modify content for a clean capture
Inject CSS or run page JavaScript before the screenshot to hide a development banner, close a modal, or set a known state:
await page.addStyleTag({
content: '.cookie-banner, .debug-toolbar { display: none !important; }'
});
await page.locator('button.dismiss').click().catch(() => {});
Use selectors that belong to your page. Hiding an element changes the captured document, so keep the script with the capture configuration for reproducibility.
PDF is different from PNG
Playwright’s screenshot API creates a raster image. If the deliverable is a paginated document, use the browser’s PDF API instead:
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
await page.pdf({
path: 'page.pdf',
format: 'A4',
printBackground: true,
margin: { top: '12mm', right: '12mm', bottom: '12mm', left: '12mm' }
});
PDF pagination, print CSS, margins, and page ranges are separate concerns from PNG dimensions. Do not expect a long full-page PNG to have the same breaks as a PDF.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Puppeteer as an alternative
Puppeteer offers the same basic browser-rendering pattern: launch a browser, create a page, navigate, and call page.screenshot(). Its official screenshots guide documents saving an image and full-page capture.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setViewport({ width: 1280, height: 800 });
await page.goto('http://localhost:3000', { waitUntil: 'networkidle0' });
await page.screenshot({ path: 'puppeteer.png', fullPage: true });
await browser.close();
})();
Choose the library already used by your project, or choose Playwright if you specifically want its VS Code testing integration. The available documentation does not establish a universal PNG-quality winner between the two; page loading, browser version, viewport, and rendering conditions have more direct impact on your result.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
ScreenshotNeo is a hosted website screenshot API and MCP server. One GET request renders a URL and returns PNG, JPEG, WebP, or PDF. It handles the browser infrastructure and, before capture, accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled.
Only clean shots are billed. 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.
Free tools Windows power users keep installed
One-click scans. No signup required.
Use the API documentation at screenshotneo.com/docs/ for authentication and options. This example targets a public HTML page; replace the URL with your own:
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 supports full-page capture with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets plus custom viewports, retina scale, custom CSS and JavaScript, click-before-capture, waits for selectors, delays or network idle, request and resource blocking, headers, cookies, user agents, Authorization, timezone, geolocation, transparent backgrounds, resizing, selectable cache TTLs, signed links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, an OpenAPI specification, and compatibility with parameter names used by other screenshot APIs.
| 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 |
Yearly billing gives two months free, and every feature is available on every plan. Create a free ScreenshotNeo account to get 1,000 screenshots a month without a card.
Best Value
Troubleshooting common failures
“Cannot find module ‘playwright’”
Install the dependency in the project folder with npm install -D playwright, then run the script from that folder. If you used the VS Code setup command, check that the terminal is opened at the same workspace.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Clear out junk files and repair common Windows errors3Scan for outdated or missing drivers - takes under a minuteBrowser executable is missing
Run npx playwright install chromium. In restricted environments, confirm that the browser download is permitted or configure Playwright to use an approved executable.
Navigation times out
Verify the local server is running and the URL and port are correct. If the page intentionally keeps connections open, avoid networkidle; wait for a concrete ready selector instead. Investigate DNS, proxy, authentication, and firewall settings when the address is not local.
The PNG is blank or missing images
Capture after the application has rendered its content. Wait for a selector, fonts, and unfinished images; ensure relative URLs resolve from an HTTP server; and check the browser console and network responses for failed assets.
The element locator fails
Confirm the selector exists in the rendered DOM, account for iframes, and wait for the element before capturing. A selector that changes between development and production should be replaced with a stable test attribute.
Captures differ between machines
Pin the browser and dependency versions where possible, use the same viewport and device scale factor, keep fonts installed consistently, and avoid animations. Headless mode, operating-system font rendering, and hardware can still affect pixels.
The full-page image is unexpectedly large
Reduce the viewport width or device scale factor only if that matches your intended output, capture a specific element, or produce a PDF when pagination is the real requirement. Do not crop away content accidentally by using a viewport screenshot for a page that scrolls.
Choosing the right workflow
- One page in an existing web project: run a short Playwright script from VS Code against the project’s local server.
- Repeatable visual checks: keep the script, browser version, viewport, fonts, and waits under version control and use stable selectors.
- A single standalone file: try a
file://URL, but switch to a local server if assets or browser security interfere. - Many URLs or no browser maintenance: use ScreenshotNeo’s API, with cleanup controls, verdict headers, caching, bulk capture, and asynchronous jobs.
- AI-assisted capture: use ScreenshotNeo’s MCP tools from an MCP-compatible client.
Frequently Asked Questions
Can VS Code convert HTML to PNG without installing a browser library?
Not through a documented built-in command. VS Code can edit and run the code, while a renderer such as Playwright, Puppeteer, or a hosted screenshot API performs the capture.
Why does my local HTML look different in the PNG?
The browser may be using a different viewport, device scale factor, font set, color scheme, or asset-loading path. Serve the project over HTTP, set these values explicitly, and wait for the page’s actual ready state.
Should I use a viewport screenshot or fullPage?
Use a viewport screenshot for exactly what fits in the selected browser window. Use fullPage: true when the image must include the page’s complete scrollable content.
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.

