| Accessibility Snapshots | Inline Clojure via --eval | Visual Annotations | Agent Scaffolding |
![]() | ![]() | ![]() | ![]() |
Playwright's Java API is imperative and verbose — option builders, checked exceptions, manual resource cleanup. Clojure deserves better. And AI agents deserve more than a shell wrapper.
spel wraps Playwright Java with idiomatic Clojure: maps for options, anomaly maps for errors, with-* macros for lifecycle, and a native CLI binary for instant browser automation. It does everything a modern agentic workflow needs — in one tool.
--eval scripting, and zero brittle CSS selectors — agents reason and act in a loop without restarting.--eval — mix business logic with automation, call any GraalVM-bound function, compose scripts on the fly. No other browser tool lets an agent write and execute real code mid-session.Clojure library:
;; deps.edn
{:deps {com.blockether/spel {:mvn/version "0.5.7"}}}
Native CLI (download from GitHub releases):
# macOS (Apple Silicon)
curl -LO https://github.com/Blockether/spel/releases/latest/download/spel-macos-arm64
chmod +x spel-macos-arm64 && mv spel-macos-arm64 ~/.local/bin/spel
# Linux (amd64)
curl -LO https://github.com/Blockether/spel/releases/latest/download/spel-linux-amd64
chmod +x spel-linux-amd64 && mv spel-linux-amd64 ~/.local/bin/spel
# Linux (arm64)
curl -LO https://github.com/Blockether/spel/releases/latest/download/spel-linux-arm64
chmod +x spel-linux-arm64 && mv spel-linux-arm64 ~/.local/bin/spel
# Windows (PowerShell)
Invoke-WebRequest -Uri https://github.com/Blockether/spel/releases/latest/download/spel-windows-amd64.exe -OutFile spel.exe
Move-Item spel.exe "$env:LOCALAPPDATA\Microsoft\WindowsApps\spel.exe"
Add ~/.local/bin to your PATH:
export PATH="$HOME/.local/bin:$PATH" # add to ~/.bashrc or ~/.zshrc
macOS Gatekeeper (binaries are not Apple-signed):
xattr -d com.apple.quarantine ~/.local/bin/spel
Post-install:
spel install # install browsers
spel version # verify installation
If you're an LLM agent installing spel for a user, follow these steps. Ask questions where indicated — adapt to the user's answers.
Detect the user's OS and architecture, then download the correct binary:
| Platform | Binary name |
|---|---|
| macOS Apple Silicon | spel-macos-arm64 |
| Linux x86_64 | spel-linux-amd64 |
| Linux ARM64 | spel-linux-arm64 |
| Windows x86_64 | spel-windows-amd64.exe |
Agent:
uname -s + uname -m (Unix) or check $env:PROCESSOR_ARCHITECTURE (Windows)https://github.com/Blockether/spel/releases/latest/download/<binary>~/.local/bin/ (Unix) or %LOCALAPPDATA%\spel\ (Windows)chmod +x (Unix only)xattr -d com.apple.quarantine"Which browser do you primarily use? Chrome, Edge, or Brave?"
Based on the answer, set the default channel:
| Answer | Channel |
|---|---|
| Chrome (default) | chrome (no flag needed) |
| Edge | msedge |
| Brave | brave |
Set SPEL_CHANNEL environment variable to the chosen channel (e.g. msedge, brave). Skip for Chrome — it's the default.
"Do you want spel to use your real browser profile? This gives you all your extensions, saved passwords, bookmarks, and preferences inside spel."
If yes, detect the profile path:
| OS | Chrome | Edge |
|---|---|---|
| macOS | ~/Library/Application Support/Google/Chrome/Default | ~/Library/Application Support/Microsoft Edge/Default |
| Linux | ~/.config/google-chrome/Default | ~/.config/microsoft-edge/Default |
| Windows | %LOCALAPPDATA%\Google\Chrome\User Data\Default | %LOCALAPPDATA%\Microsoft\Edge\User Data\Default |
Verify the profile exists, then set:
Set SPEL_PROFILE environment variable to the detected path.
If no, skip — spel will use a fresh browser context each time.
"Are you behind a corporate proxy (Zscaler, Netskope, etc.)? If unsure, try
spel installfirst — if it fails with 'PKIX path building failed', you need this."
If yes:
Set both SPEL_CA_BUNDLE and NODE_EXTRA_CA_CERTS environment variables to the path of the corporate CA bundle (PEM format). Ask the user where the cert file is, or help them find it.
See the Corporate Proxy section below for details.
Run spel install to download Chromium. If the user chose Edge, also run spel install msedge.
"Will you use spel for automation only (scripting, scraping, agents) or also for writing tests (with assertions and Allure reports)?"
Scaffold agent skills (all 8 agents by default — use --only to scaffold a subset, or --no-tests for automation-only):
# Full scaffolding with all 8 agents (default)
spel init-agents
# Automation only — SKILL reference only, no test scaffolding
spel init-agents --no-tests
# Scaffold only specific groups
spel init-agents --only=test # test agents only
spel init-agents --only=automation # browser automation agents only
spel init-agents --only=visual # visual QA agents only
Choose the right loop for your coding agent:
spel init-agents --loop=opencode # OpenCode (default)
spel init-agents --loop=claude # Claude Code
Run spel version to confirm installation. Then test with spel open https://example.com — it should open and return JSON. Run spel close after.
If the user chose to use a profile, test with their profile path to verify it works.
Save the user's choices so they don't need to pass flags every time. Detect their shell and OS, then write to the correct config file:
| OS | Shell | Config file |
|---|---|---|
| macOS/Linux | zsh | ~/.zshrc |
| macOS/Linux | bash | ~/.bashrc |
| macOS/Linux | fish | ~/.config/fish/config.fish (use set -Ux VAR value) |
| Windows | PowerShell | $PROFILE (use [Environment]::SetEnvironmentVariable("VAR", "value", "User")) |
| Windows | cmd | setx VAR value |
Agent: detect the shell (echo $SHELL on Unix, $PSVersionTable on Windows) and write the env vars to the correct file using the correct syntax. Do not assume bash.
If you're behind a corporate SSL-inspecting proxy (Zscaler, Netskope, etc.), spel install may fail with "PKIX path building failed". Set these env vars before running spel install:
| Env Var | Format | On missing file | Description |
|---|---|---|---|
SPEL_CA_BUNDLE | PEM file | Error | Extra CA certs (merged with defaults) |
NODE_EXTRA_CA_CERTS | PEM file | Warning, skips | Shared with Node.js subprocess |
SPEL_TRUSTSTORE | JKS/PKCS12 | Error | Truststore (merged with defaults) |
SPEL_TRUSTSTORE_TYPE | String | — | Default: JKS |
SPEL_TRUSTSTORE_PASSWORD | String | — | Default: empty |
export SPEL_CA_BUNDLE=/path/to/corporate-ca.pem
export NODE_EXTRA_CA_CERTS=/path/to/corporate-ca.pem
spel install --with-deps
All options merge with built-in defaults — public CDN certs continue to work.
All env vars are optional. CLI flags always take priority over env vars.
| Env Var | CLI equivalent | Description |
|---|---|---|
| Browser | ||
SPEL_CHANNEL | --channel | Browser channel: chrome, msedge, brave |
SPEL_PROFILE | --profile | Chrome/Edge user data directory (full profile: extensions, passwords, bookmarks) |
SPEL_LOAD_STATE | --load-state | Playwright storage state JSON path (alias: SPEL_STORAGE_STATE) |
SPEL_EXECUTABLE_PATH | --executable-path | Custom browser binary path |
SPEL_USER_AGENT | --user-agent | Custom user agent string |
SPEL_STEALTH | --no-stealth | Set to false to disable stealth mode (ON by default) |
| Session | ||
SPEL_SESSION | --session | Session name (default: default) |
SPEL_JSON | --json | Set to true for JSON output |
SPEL_TIMEOUT | --timeout | Command timeout in milliseconds |
| Network | ||
SPEL_PROXY | --proxy | Proxy server URL |
SPEL_PROXY_BYPASS | --proxy-bypass | Proxy bypass patterns |
SPEL_HEADERS | --headers | Default HTTP headers (JSON string) |
SPEL_IGNORE_HTTPS_ERRORS | --ignore-https-errors | Set to true to ignore HTTPS errors |
| SSL/TLS | ||
SPEL_CA_BUNDLE | — | PEM file with extra CA certs (merged with defaults) |
NODE_EXTRA_CA_CERTS | — | PEM file, also respected by Node.js subprocess |
SPEL_TRUSTSTORE | — | JKS/PKCS12 truststore path |
SPEL_TRUSTSTORE_TYPE | — | Truststore type (default: JKS) |
SPEL_TRUSTSTORE_PASSWORD | — | Truststore password |
| Advanced | ||
SPEL_CDP | --cdp | Connect via Chrome DevTools Protocol URL |
SPEL_ARGS | --args | Extra Chromium launch args (comma-separated) |
SPEL_DEBUG | --debug | Set to true for debug logging |
(require '[com.blockether.spel.core :as core]
'[com.blockether.spel.page :as page])
(core/with-testing-page [pg]
(page/navigate pg "https://example.org")
(page/title pg))
;; => "Example Domain"
Pass an opts map for device emulation:
(core/with-testing-page {:device :iphone-14 :locale "fr-FR"} [pg]
(page/navigate pg "https://example.org"))
For explicit lifecycle control, with-playwright/with-browser/with-context/with-page nesting is available. See the full API reference.
Browser testing:
(core/with-testing-page [pg]
(page/navigate pg "https://example.org")
(page/title pg))
API testing:
(core/with-testing-api {:base-url "https://api.example.org"} [ctx]
(core/api-get ctx "/users"))
Combined UI + API — use page-api or with-page-api to share a single Playwright trace:
;; page-api: same context, same trace
(core/with-testing-page [pg]
(page/navigate pg "https://example.org/login")
(core/api-get (core/page-api pg) "/api/me"))
;; with-page-api: same context, different base-url
(core/with-testing-page [pg]
(page/navigate pg "https://example.org/login")
(core/with-page-api pg {:base-url "https://api.example.org"} [ctx]
(core/api-get ctx "/me")))
Important: Do NOT nest
with-testing-pageinsidewith-testing-api(or vice versa). Each creates its own Playwright instance, browser, and context — you get two separate traces instead of one. Usepage-api/with-page-apito combine UI and API testing under a single trace.
See SKILL.md for fixtures, steps, and attachments.
See SKILL.md for fixtures, steps, and attachments.
spel compiles to a native binary via GraalVM - no JVM startup, instant execution. The CLI provides commands for browser automation (open, screenshot, snapshot, annotate), a persistent browser daemon, session recording (codegen), PDF generation, and an --eval mode for inline Clojure scripting via SCI. Run spel --help for the full command list.
Point your AI agent at spel and let it write your E2E tests.
spel init-agents # all 8 agents, OpenCode (default)
spel init-agents --loop=claude # Claude Code
spel init-agents --only=test # test agents only
spel init-agents --only=automation # browser automation agents only
spel init-agents --only=visual # visual QA agents only
spel init-agents --only=test,visual # combine groups with commas
spel init-agents --flavour=clojure-test # clojure.test instead of Lazytest
spel init-agents --no-tests # SKILL only (interactive dev)
| Flag | Default | Purpose |
|---|---|---|
--loop TARGET | opencode | Agent format: opencode, claude (vscode is deprecated) |
--only GROUPS | — | Scaffold only specific agent groups (comma-separated): test, automation, visual |
--ns NS | dir name | Base namespace for generated tests |
--flavour FLAVOUR | lazytest | Test framework: lazytest or clojure-test |
--no-tests | — | Scaffold only the SKILL (API reference) — no test agents |
--dry-run | — | Preview files without writing |
--force | — | Overwrite existing files |
--test-dir DIR | test-e2e | E2E test output directory |
--specs-dir DIR | test-e2e/specs | Test plans directory |
Record browser sessions as WebM files for debugging and CI artifacts.
(def ctx (core/new-context browser {:record-video-dir "videos"}))
See recording options and test fixtures.
Record browser sessions and transform them to idiomatic Clojure code.
spel codegen record -o recording.jsonl https://example.org
spel codegen recording.jsonl > my_test.clj
See full actions and output formats.
# Install browsers (via Playwright Java CLI)
clojure -M -e "(com.microsoft.playwright.CLI/main (into-array String [\"install\" \"--with-deps\"]))"
# Build JAR
clojure -T:build jar
# Build native image (requires GraalVM)
clojure -T:build native-image
# Run tests
make test
make test-allure
# Start REPL
make repl
See CHANGELOG.md.
Apache License 2.0 — see LICENSE.
Can you improve this documentation? These fine people already did:
Karol Wojcik, Michał Kruk & blockether-deployerEdit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |