Selenium WebDriver is an open-source browser automation project whose client libraries send commands, such as "open this URL" or "take a screenshot", over the W3C WebDriver protocol to a driver program that controls a real installed browser.
It is the oldest of the three tools most teams use to script browsers, and the only one built on a W3C standard. This entry is part of the screenshot glossary.
What it means in plain terms
Selenium has three parts:
- Language bindings. Libraries for Java, Python, C#, JavaScript and Ruby. You write
driver.get(url). - A driver. A small server per browser: ChromeDriver, geckodriver for Firefox, safaridriver, msedgedriver. It turns WebDriver commands into the browser's own automation calls.
- The browser. A normal Chrome, Firefox, Safari or Edge, headless or with a window.
Your code never talks to the browser directly. It talks HTTP to the driver, and the driver talks to the browser. That extra hop is why Selenium supports more browsers than any other tool, and why each command is a little slower than in Puppeteer or Playwright.
How it works technically
flowchart LR A[Your test or script<br>selenium-webdriver] -- W3C WebDriver<br>JSON over HTTP --> B[ChromeDriver] B -- DevTools protocol --> C[Chrome] A -. WebDriver BiDi<br>WebSocket, optional .-> C D[Puppeteer / Playwright] -- one WebSocket,<br>CDP or own protocol --> C
Every command is an HTTP request: POST /session/{id}/url to navigate, GET /session/{id}/screenshot to capture. The screenshot comes back as a base64 PNG string. Puppeteer and Playwright skip the driver and keep one WebSocket open to the browser, which also lets them react to events (network requests, console messages) as they happen. Selenium is adding the same two-way channel through WebDriver BiDi.
The standard defines two screenshot commands: the viewport (takeScreenshot) and one element. There is no full-page command in the standard.
A test run: the window is not the viewport
We ran this script with Chrome in headless mode and --window-size=1200,630:
import { Builder, By } from 'selenium-webdriver'; import chrome from 'selenium-webdriver/chrome.js'; import { writeFile } from 'node:fs/promises'; const options = new chrome.Options().addArguments('--headless=new', '--window-size=1200,630'); const driver = await new Builder().forBrowser('chrome').setChromeOptions(options).build(); try { await driver.get('https://example.com'); const inner = await driver.executeScript('return [innerWidth, innerHeight]'); console.log('viewport', inner.join('x')); await writeFile('page.png', await driver.takeScreenshot(), 'base64'); // viewport const p = await driver.findElement(By.css('p')); await writeFile('paragraph.png', await p.takeScreenshot(), 'base64'); // one element } finally { await driver.quit(); }
It printed viewport 1200x487, and page.png was 1200×487, not 1200×630. --window-size sets the outer window, and part of it is the browser's own frame. If you need an exact image size, set the viewport through the DevTools protocol (Chromium only):
await driver.sendDevToolsCommand('Emulation.setDeviceMetricsOverride', { width: 1200, height: 630, deviceScaleFactor: 1, mobile: false, }); await driver.get('https://example.com'); // innerWidth x innerHeight is now 1200x630
Tested on 2026-10-15 with Node 22.18.0, [email protected] and Chrome 155.0.8059.40 on macOS. No driver was installed by hand; Selenium Manager supplied ChromeDriver. The second script printed viewport 1200x630 and saved a 1200×630 PNG. The Selenium alternative page measures what this trap and others cost in a real job.
The same capture in Puppeteer, Playwright and RenderScreenshot
The three other tools set the viewport, not the window, so the size you ask for is the size you get.
// Puppeteer await page.setViewport({ width: 1200, height: 630 }); await page.goto('https://example.com'); await page.screenshot({ path: 'page.png' }); // Playwright const page = await browser.newPage({ viewport: { width: 1200, height: 630 } }); await page.goto('https://example.com'); await page.screenshot({ path: 'page.png', fullPage: true }); // full page built in
These are fragments; each assumes a launched browser (and for Puppeteer a page). With RenderScreenshot there is no browser or driver to manage; the size comes from a preset or width and height:
curl -sS --fail "https://api.renderscreenshot.com/v1/screenshot?url=https://example.com&width=1200&height=630" \ -H "Authorization: Bearer $RS_API_KEY" -o page.png
Add full_page=true for the whole scrollable page. The viewport options list the limits.
When Selenium is the right choice
- You must test in Safari, or in a specific browser version a client uses.
- Your team already has a Selenium Grid and tests in Java or C#.
- You need a W3C-standard protocol, for example for a cloud device farm.
For screenshot jobs alone, the driver hop, the window-size trap and the missing full-page command make it the hardest of the options. The Playwright vs Selenium comparison goes through the trade-off.
Common mistakes
- Using
--window-sizeas the image size. In our test, the image was 143 px shorter. - Forgetting
driver.quit(). The driver and the browser keep running after your script fails. Put it infinally. - Pinning an old ChromeDriver by hand. It stops matching the moment Chrome updates. Let Selenium Manager resolve it.
- Fixed sleeps. Use explicit waits (
until.elementLocated) instead ofdriver.sleep. - Saving the base64 string as text. Decode it:
writeFile(path, data, 'base64').
Related terms
- WebDriver BiDi: the two-way protocol Selenium is moving to.
- Chrome DevTools Protocol: what ChromeDriver, Puppeteer and Playwright use underneath.
- Headless browser: the mode most screenshot jobs run in.
- Playwright and Puppeteer: the driver-less alternatives.
For step-by-step Selenium captures in several languages, read Selenium screenshots. To skip the driver entirely, get a free API key.