Recommended Free Tools
Use PHP WebDriver’s DesiredCapabilities::htmlUnitWithJS() to request an HtmlUnit session with JavaScript enabled, then call takeScreenshot() to save the current view. This works only when the Selenium remote end you connect to accepts the HtmlUnit capability and implements the screenshot command; the PHP package does not install HtmlUnit or start Selenium for you.
What you need before writing PHP
- PHP with Composer available on the machine running your script.
- The Composer package
php-webdriver/webdriver. - A running WebDriver remote end, such as the Selenium Server or another endpoint configured to accept
browserName=htmlunit. - An endpoint path and port that match your installed Selenium version.
http://localhost:4444below is only an illustrative local address.
The project documents compatibility with Selenium Server 2.x, 3.x and 4.x, and with both W3C WebDriver and the older JsonWireProtocol. That documented range is not a guarantee that every capability combination behaves identically on every server release. Confirm the URL and routing path required by your deployment.
Install the PHP client
composer require php-webdriver/webdriver
Older tutorials may refer to facebook/php-webdriver. The project changed its package name to php-webdriver/webdriver beginning with library version 1.8.0, while the PHP namespaces remain under FacebookWebDriver.
How htmlUnitWithJS() works
DesiredCapabilities::htmlUnitWithJS() creates a capability object whose browser name is htmlunit and whose HtmlUnit-specific JavaScript setting is enabled. It requests a session; it does not download HtmlUnit, launch a server, or prove that the remote end can take screenshots.
#1 Best Overall
The JavaScript setting is HtmlUnit-specific. In the PHP client source, attempting to apply that setting after selecting a different browser name results in an unsupported-operation exception. Use the factory when you specifically want an HtmlUnit session with JavaScript enabled.
Complete PHP example
The following script opens a page, asks the remote end for an HtmlUnit-with-JavaScript session, saves the current-view screenshot as a PNG, and always closes the session.
<?php
require_once __DIR__ . '/vendor/autoload.php';
use FacebookWebDriverRemoteDesiredCapabilities;
use FacebookWebDriverRemoteRemoteWebDriver;
$serverUrl = 'http://localhost:4444';
$driver = RemoteWebDriver::create(
$serverUrl,
DesiredCapabilities::htmlUnitWithJS()
);
try {
$driver->get('https://example.com');
$driver->takeScreenshot(__DIR__ . '/screenshot.png');
} finally {
$driver->quit();
}
Run it from the directory containing vendor/. If the server uses a version-specific path instead of the root URL shown, replace $serverUrl with the path documented for that Selenium installation. A successful run writes screenshot.png beside the PHP file, provided the endpoint implements both the requested session and screenshot command.
Keep the session alive while diagnosing failures
For troubleshooting, temporarily move the screenshot and navigation calls inside a more verbose try block and log the exception message and stack trace. Restore the finally block before production use so abandoned sessions do not accumulate on the remote server.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Save screenshot data in memory
Passing a filename is optional. The documented no-argument form returns the screenshot data:
$driver->get('https://example.com');
$screenshotData = $driver->takeScreenshot();
file_put_contents(__DIR__ . '/screenshot.png', $screenshotData);
This is useful when you need to upload the bytes, attach them to a test report, or run your own image processing instead of writing directly from the WebDriver client.
Capture one element instead of the current view
Locate an element, then use the element screenshot method:
$element = $driver->findElement(
FacebookWebDriverWebDriverBy::cssSelector('#main')
);
$element->takeElementScreenshot(__DIR__ . '/main.png');
As with the driver method, omit the path to retrieve the returned data and save or process it yourself. Element screenshot behavior still depends on the remote end’s implementation.
What “screenshot” means here
The PHP command reference describes driver screenshots as screenshots of the current view and element screenshots as screenshots of a selected element. Selenium’s general API describes screenshot output as base64-encoded PNG data and gives a best-effort scope: the entire page, then the current window, then the visible portion of the current frame, and finally the entire display containing the browser.
That general description is not proof that an HtmlUnit endpoint supports every scope. Do not assume that the resulting PNG is a full-page capture. If your requirement is a complete long page, a precise viewport, or browser-specific layout fidelity, test the exact endpoint and version you operate. Compare the output with a real Chrome or Firefox driver when visual accuracy matters.
HtmlUnit JavaScript and rendering limits
HtmlUnit simulates a configured browser rather than running the same rendering engine as Chrome or Firefox. Its documentation describes JavaScript execution when a page loads or when a handler is triggered and lists tested examples such as htmx 1.7.0, 1.8.4, 1.9.x and 2.0.x, and jQuery 1.8.2, 1.11.3 and 1.12.4.
Those entries are project-tested library examples, not a universal compatibility percentage. A site may still depend on APIs, CSS behavior, fonts, canvas output, WebGL, media, or browser security behavior that HtmlUnit does not reproduce. Use HtmlUnit when simulated, script-enabled page access is sufficient; use an actual browser driver when production-browser pixels are the acceptance criterion.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Choosing and validating the remote end
Check capability negotiation
At session creation, verify that the server accepts browserName=htmlunit and the JavaScript capability generated by htmlUnitWithJS(). A session-creation error usually means the endpoint does not provide that browser, the capability spelling is rejected, or the request reached the wrong server path.
Check screenshot support separately
A server can accept a session yet reject takeScreenshot. Run a minimal navigation-and-capture test before building a larger workflow. Also test element screenshots if your application needs them; support for one does not establish support for every screenshot scope.
Check version and deployment fit
Match the Composer client, Selenium Server or driver, protocol mode, and endpoint URL used in production. The PHP README’s broad Selenium compatibility statement should be read as documented project support, not as a promise for an unverified HtmlUnit pairing.
Common errors and fixes
“Could not start a new session”
- Cause: the URL points to the wrong port or path, or the remote end does not accept the HtmlUnit capability.
- Fix: confirm the server is running, copy its exact WebDriver endpoint, and inspect its session logs for the received capabilities.
Unsupported operation for JavaScript capability
- Cause: HtmlUnit’s JavaScript setting was applied to a capability object whose browser name is not
htmlunit. - Fix: create the object with
DesiredCapabilities::htmlUnitWithJS()rather than changing a Chrome, Firefox, or other capability after the fact.
Session starts but screenshot fails
- Cause: this remote end does not implement screenshot capture, or its implementation does not support the requested scope.
- Fix: verify the endpoint’s command support, test
takeScreenshot()immediately after navigation, and switch to an actual browser driver when the endpoint cannot provide the required image.
The file is missing or empty
- Cause: the PHP process cannot write to the target directory, the script exits before the call, or returned data was not written correctly.
- Fix: use an absolute writable path, check the return value of
file_put_contents(), and log exceptions before thefinallyblock quits the driver.
The page is blank or incomplete
- Cause: navigation, scripts, asynchronous requests, or site-specific browser assumptions have not completed or are not supported by HtmlUnit.
- Fix: inspect the loaded page and server logs, add an application-appropriate wait, and validate the same URL in a real browser driver if rendering fidelity is required.
Reliability and performance practices
- Create one driver per controlled workflow and always call
quit(); repeatedly creating abandoned sessions can exhaust the remote server. - Capture after navigation has reached the state your test needs. A screenshot call does not guarantee that every asynchronous request or animation has finished.
- Use in-memory bytes when the next step is an upload or test assertion, avoiding an unnecessary temporary file.
- Keep a small capability-and-screenshot smoke test in continuous integration so an endpoint upgrade cannot silently remove HtmlUnit or screenshot support.
- Record the server URL, Selenium version, PHP client version, requested capability, and failure response when diagnosing intermittent behavior.
Or skip the browser setup
If your goal is a dependable website image rather than an HtmlUnit experiment, ScreenshotNeo provides a single HTTP request for PNG, JPEG, WebP, or PDF output. Before capture it accepts the cookie or consent banner as a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. 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. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.
See the ScreenshotNeo documentation for all options. 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
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}`);
It supports full-page captures with lazy images loaded, CSS-selector element capture, dark mode, device presets and custom viewports, retina scale, PDF paper and page controls, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, selectable-TTL caching, signed image links, asynchronous webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. Parameter names used by other screenshot APIs also work, easing migration.
The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; yearly billing provides two months free, and every feature is available on every plan. Create a free ScreenshotNeo account to start.
Which approach should you use?
| Requirement | PHP WebDriver with HtmlUnitWithJS | ScreenshotNeo |
|---|---|---|
| Primary purpose | Drive a WebDriver session from PHP | Request a rendered image or PDF over HTTP |
| Infrastructure | You operate a compatible remote end | Use the hosted API or MCP server |
| Rendering model | HtmlUnit simulation with its supported JavaScript behavior | Configured capture options for page, element, device and document output |
| Failure billing | Depends on your infrastructure; no API billing rule applies | Failed loads, bot checks, blank pages, timeouts and cache hits are not billed |
Frequently Asked Questions
Does the PHP package install HtmlUnit?
No. Composer installs the PHP WebDriver client. You must provide a remote end that accepts the HtmlUnit capability.
PC 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 & 11Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchIs an HtmlUnit screenshot always full-page?
No. Screenshot scope is endpoint-dependent; test the exact server and version rather than assuming a full-page image.
Can I use this code with Chrome or Firefox?
Not with the HtmlUnit-specific factory. Use the capabilities appropriate to the browser and driver you deploy, and do not apply HtmlUnit’s JavaScript setting to another browser capability.
What image format does WebDriver return?
Selenium’s general screenshot API describes base64-encoded PNG data. The PHP client can save that data to a file or return it in memory.
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.
Free tools Windows power users keep installed
One-click scans. No signup required.

