By Sagar Shankaran, Founder of CallSphere
A detailed comparison of Selenium, Playwright, and Puppeteer for building AI-powered browser agents. Covers async support, multi-browser compatibility, recording capabilities, and ease of AI integration.
Key takeaways
Every AI web agent needs a way to control a browser. The agent decides what to click, type, or navigate to, but some library has to translate those decisions into actual browser commands. Three tools dominate this space: Selenium, Playwright, and Puppeteer. Each was built for a different era and a different set of assumptions, and those differences matter significantly when you are building an AI-powered agent rather than a traditional test suite.
The right choice depends on your language preference, whether you need async-first architecture, how many browser engines you need to support, and how tightly you want to integrate with your LLM reasoning loop.
Before diving into details, here is a high-level comparison of the three tools across the dimensions that matter most for AI agents.
flowchart LR
GOAL(["High level goal"])
PLAN["Planner LLM"]
SCREEN["Screen capture<br/>every step"]
VLM["Vision LLM<br/>reads UI state"]
ACT{"Action type"}
CLICK["Click coordinate"]
TYPE["Type text"]
KEY["Keyboard shortcut"]
GUARD["Safety filter<br/>allow lists"]
OS[("OS sandbox<br/>ephemeral VM")]
DONE(["Goal verified"])
GOAL --> PLAN --> SCREEN --> VLM --> ACT
ACT --> CLICK --> GUARD
ACT --> TYPE --> GUARD
ACT --> KEY --> GUARD
GUARD --> OS --> SCREEN
OS --> DONE
style PLAN fill:#4f46e5,stroke:#4338ca,color:#fff
style GUARD fill:#f59e0b,stroke:#d97706,color:#1f2937
style OS fill:#ede9fe,stroke:#7c3aed,color:#1e1b4b
style DONE fill:#059669,stroke:#047857,color:#fff
comparison = {
"Selenium": {
"language_support": ["Python", "Java", "C#", "Ruby", "JS"],
"browsers": ["Chrome", "Firefox", "Safari", "Edge"],
"async_native": False,
"auto_wait": False,
"network_interception": "limited",
"built_in_recording": False,
"protocol": "WebDriver (W3C)",
"first_release": 2004,
},
"Playwright": {
"language_support": ["Python", "JS/TS", "Java", "C#"],
"browsers": ["Chromium", "Firefox", "WebKit"],
"async_native": True,
"auto_wait": True,
"network_interception": "full",
"built_in_recording": True, # codegen
"protocol": "CDP + custom",
"first_release": 2020,
},
"Puppeteer": {
"language_support": ["JS/TS", "Python (pyppeteer)"],
"browsers": ["Chromium", "Firefox (experimental)"],
"async_native": True,
"auto_wait": False,
"network_interception": "full",
"built_in_recording": False,
"protocol": "CDP",
"first_release": 2017,
},
}
Selenium has been the standard browser automation tool for two decades. Its biggest advantage is breadth — it supports every major programming language and every major browser through the standardized W3C WebDriver protocol. If you need to automate Safari or run tests in a corporate environment that mandates Selenium Grid, it is the only option.
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
For AI agents, Selenium has significant drawbacks. Its API is synchronous by default, which means your agent loop blocks while waiting for page loads and element interactions. It lacks built-in auto-waiting, so you need explicit waits everywhere. And its error messages when elements are not found are often cryptic.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
def selenium_agent_step(driver: webdriver.Chrome, task: str):
"""Single agent step with Selenium — note the manual waits."""
# Must explicitly wait for elements
wait = WebDriverWait(driver, 10)
# Get page state for LLM
page_source = driver.page_source
current_url = driver.current_url
title = driver.title
# After LLM decides to click a button
target_selector = "button.submit-order"
try:
element = wait.until(
EC.element_to_be_clickable((By.CSS_SELECTOR, target_selector))
)
element.click()
except Exception as e:
# Selenium errors are often hard to parse programmatically
print(f"Element interaction failed: {e}")
# Screenshot for vision-based agents
driver.save_screenshot("/tmp/step_screenshot.png")
Playwright was built by the team that originally created Puppeteer, and it shows. It is async-native in Python, has built-in auto-waiting (every action waits for the element to be visible, enabled, and stable before interacting), supports all three major browser engines, and includes powerful network interception capabilities.
For AI agents, Playwright is the strongest choice in most scenarios. The async API integrates naturally with async LLM clients. Auto-waiting eliminates an entire class of flaky failures. And the codegen tool can record user interactions to bootstrap agent workflows.
import asyncio
from playwright.async_api import async_playwright
async def playwright_agent_loop(task: str, max_steps: int = 20):
"""AI agent loop using Playwright — async and auto-waiting."""
async with async_playwright() as p:
browser = await p.chromium.launch(headless=True)
context = await browser.new_context(
viewport={"width": 1280, "height": 720},
record_video_dir="/tmp/agent_videos",
)
page = await context.new_page()
# Intercept network requests for context
responses = []
page.on("response", lambda r: responses.append({
"url": r.url, "status": r.status
}))
await page.goto("https://example.com")
for step in range(max_steps):
# Capture state — no manual waits needed
screenshot = await page.screenshot()
title = await page.title()
url = page.url
# Get accessibility tree for better LLM understanding
acc_tree = await page.accessibility.snapshot()
# LLM decides action
action = await get_next_action(
screenshot=screenshot,
accessibility_tree=acc_tree,
task=task,
url=url,
)
# Playwright auto-waits for actionability
if action["type"] == "click":
await page.click(action["selector"])
elif action["type"] == "fill":
await page.fill(action["selector"], action["value"])
elif action["type"] == "select":
await page.select_option(
action["selector"], action["value"]
)
elif action["type"] == "done":
break
await context.close()
await browser.close()
Puppeteer offers a clean async API with direct Chrome DevTools Protocol access. Its primary limitation for AI agents is that it only officially supports Chromium-based browsers. The Python ecosystem support through pyppeteer is also less maintained than Playwright's official Python bindings.
Where Puppeteer shines is in scenarios where you need low-level CDP access — for example, intercepting WebSocket frames, modifying JavaScript execution contexts, or profiling rendering performance. If your agent needs to do something that the higher-level Playwright API does not expose, Puppeteer gives you the escape hatch.
import asyncio
from pyppeteer import launch
async def puppeteer_agent_step():
"""Puppeteer-based agent step via pyppeteer."""
browser = await launch(
headless=True,
args=["--no-sandbox", "--disable-setuid-sandbox"],
)
page = await browser.newPage()
await page.setViewport({"width": 1280, "height": 720})
await page.goto("https://example.com")
# Direct CDP access for advanced features
cdp = await page.target.createCDPSession()
await cdp.send("Network.enable")
# Get DOM snapshot for LLM
dom_result = await cdp.send("DOMSnapshot.captureSnapshot", {
"computedStyles": ["display", "visibility"],
})
screenshot = await page.screenshot({"encoding": "base64"})
await browser.close()
return screenshot, dom_result
Choose Playwright if you are building a new AI agent from scratch, need async Python support, and want the most robust out-of-the-box experience. Choose Selenium if you must support Safari, work within an existing Selenium Grid infrastructure, or your team already has deep Selenium expertise. Choose Puppeteer if you are building in JavaScript/TypeScript and need low-level CDP access for advanced browser instrumentation.
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
For most AI agent projects in 2026, Playwright is the default recommendation. Its auto-waiting, native async support, multi-browser coverage, and accessibility tree API make it the most natural fit for LLM-driven browser control.
Yes, but there is rarely a good reason to. Both control a browser instance, and running two browser automation frameworks adds complexity. If you need Safari support (Selenium) and modern async patterns (Playwright), consider splitting your test suites rather than mixing frameworks.
Yes. Playwright ships with pre-built browser binaries and works reliably in Docker. Use playwright install --with-deps chromium during your Docker build to install the browser and its OS-level dependencies. This is the standard approach for running AI agents in containerized environments.
Playwright has the strongest built-in accessibility tree support via page.accessibility.snapshot(). This is particularly valuable for AI agents because the accessibility tree provides a structured, semantic representation of the page that LLMs can reason about more effectively than raw HTML.
#Selenium #Playwright #Puppeteer #BrowserAutomation #AIAgents #WebTesting #AsyncPython #DevToolsProtocol

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
A founder's guide to the personal AI assistant market: best AI assistant apps, business-grade options, and how CallSphere's voice agent fits in.
A founder's guide to free AI agents, low-code AI agent builders, and how to know when you should pay for a real platform like CallSphere.
Graphiti is the open-source temporal knowledge graph for AI agents in 2026. Learn how bi-temporal memory beats vector RAG for voice agents and long-running LLMs.
Chatbot app vs ChatGPT in 2026: a founder's clear take on the difference, when to use which, and how a real AI chatbot app development works.
How we built a fault-tolerant HVAC emergency triage and tech-dispatch platform on Kubernetes — three-tier CQRS, 11 micro-agents on the OpenAI Agents SDK + LangGraph, NATS JetStream, DTMF/SMS/WebSocket acceptance, circuit breakers, and an evaluation pipeline that catches regressions before they wake a tech at 3 AM.
Head-to-head: OpenAI Frontier and Anthropic's managed agent stack — strengths, fit, and what each means for enterprise AI voice and chat deployment.
© 2026 CallSphere Inc. All rights reserved.
Made within San Francisco
Watch how CallSphere handles real customer calls, schedules appointments, and processes payments — live.