Elixir browser automation for developers who work in WSL but need visible Windows browsers
You're developing in WSL. You need to automate a browser. But when you launch Chromium from WSL, you get a headless process or an invisible window buried in a virtual display. You can't see what's happening. You can't debug visually. You can't demo to anyone.
Playwriter runs Playwright directly on Windows via PowerShell, controlled from your Elixir code in WSL. The browser opens on your Windows desktop where you can see it. Click buttons, fill forms, take screenshots—all visible in real-time. No server setup, no firewall rules, no network configuration.
# This browser opens on your Windows desktop - visible!
{:ok, html} = Playwriter.fetch_html("https://example.com", mode: :windows)Add to your mix.exs:
def deps do
[{:playwriter, "~> 0.1.0"}]
endThen run setup:
mix deps.get
mix playwriter.setup# Fetch HTML from a page
{:ok, html} = Playwriter.fetch_html("https://example.com")
# Take a screenshot
{:ok, png} = Playwriter.screenshot("https://example.com")
File.write!("screenshot.png", png)One-time setup on Windows:
# Run from WSL - installs Playwright in Windows temp directory
powershell.exe -ExecutionPolicy Bypass -File priv/scripts/start_server.ps1 -InstallUse from Elixir:
{:ok, html} = Playwriter.fetch_html("https://example.com", mode: :windows)A browser window opens on your Windows desktop. You watch it navigate. You see the page load.
For complex workflows, use with_browser/2:
{:ok, result} = Playwriter.with_browser([mode: :windows], fn ctx ->
# Navigate
:ok = Playwriter.goto(ctx, "https://example.com/login")
# Fill form
:ok = Playwriter.fill(ctx, "#username", "myuser")
:ok = Playwriter.fill(ctx, "#password", "secret")
# Click submit
:ok = Playwriter.click(ctx, "button[type=submit]")
# Wait and get result
Process.sleep(1000)
{:ok, html} = Playwriter.content(ctx)
html
end)The browser stays open for the entire session. You see every action happen.
| Mode | Use Case | Browser Location |
|---|---|---|
:local (default) |
CI/CD, headless scraping | Same machine as Elixir |
:windows |
WSL development, debugging, demos | Windows desktop (visible) |
:remote |
Distributed automation (advanced) | Remote server |
# Local mode - fast, headless
Playwriter.fetch_html(url)
Playwriter.fetch_html(url, mode: :local)
# Windows mode - visible on Windows desktop (recommended for WSL)
Playwriter.fetch_html(url, mode: :windows)Playwriter.fetch_html(url,
mode: :windows, # :local, :windows, or :remote
headless: false, # true for invisible, false to watch
browser_type: :chromium, # :chromium (only for :windows mode)
timeout: 30_000 # milliseconds
)┌───────────────────────────────────────────────────┐
│ WSL │
│ ┌─────────────────────────────────────────────┐ │
│ │ Your Elixir Application │ │
│ │ │ │
│ │ Playwriter.fetch_html(url, mode: :windows) │ │
│ └─────────────────────┬───────────────────────┘ │
│ │ stdin/stdout │
│ │ (via PowerShell) │
└────────────────────────┼──────────────────────────┘
│
┌────────────────────────┼─────────────────────────┐
│ ▼ Windows │
│ ┌────────────────────────────────────────────┐ │
│ │ Node.js + Playwright │ │
│ └─────────────────────┬──────────────────────┘ │
│ │ │
│ ┌────────────────────────────────────────────┐ │
│ │ Browser Window │ │
│ │ (Visible on your desktop) │ │
│ └────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────┘
The :windows mode bypasses WSL2's Hyper-V networking entirely by communicating via PowerShell stdin/stdout.
- Getting Started - Installation and first steps
- Architecture - How Playwriter works
- Transport Layer - Local, Windows, and Remote modes
- WSL-Windows Integration - Detailed setup guide
- Function Reference - Complete function documentation
- Examples - Real-world usage patterns
- Troubleshooting - Common issues and fixes
Use Playwriter when:
- You develop in WSL but need to see browsers on Windows
- You're debugging web scraping and need visual feedback
- You want to demo browser automation to stakeholders
- You need to interact with sites that require JavaScript rendering
Use something else when:
- You only need headless automation (use
playwright_exdirectly) - You're not in a WSL environment
- You need maximum performance (local mode is faster)
Issues and PRs welcome at github.com/nshkrdotcom/playwriter.
MIT License. See LICENSE for details.