WSL-Windows Integration

Copy Markdown View Source

This guide covers using Playwriter to control visible browsers on Windows from Elixir running in WSL (Windows Subsystem for Linux).

Why WSL-Windows Integration?

When developing in WSL, you often want to:

  • See the browser - Watch your automation in real-time for debugging
  • Test visual behavior - Verify rendering, animations, and UI
  • Debug interactively - Pause and inspect the browser state
  • Demo to stakeholders - Show browser automation to non-technical users

The challenge: browsers launched from WSL run headless or in a virtual display, and WSL2's Hyper-V networking blocks most connection attempts to Windows. Playwriter solves this with the :windows mode.

Architecture


                         WSL 2                              
     
                Your Elixir Application                   
                                                          
     Playwriter.fetch_html("https://example.com",         
       mode: :windows)                                    
     
                                                           
                           Erlang Port                     
                           (stdin/stdout via PowerShell)   

                           

                                              Windows      
     
                PowerShell + Node.js                      
                (runs Playwright directly)                
     
                                                           
                                                           
     
                Browser Window                            
                (Visible on Windows Desktop)              
     

The :windows mode bypasses WSL2's Hyper-V firewall entirely by communicating via PowerShell stdin/stdout instead of network sockets.

Setup

1. Install Node.js on Windows

The Playwright driver runs on Node.js. Install Node.js on Windows (not WSL):

# Using winget
winget install OpenJS.NodeJS.LTS

# Or download from https://nodejs.org

2. Install Playwright on Windows

Run the setup script from WSL:

# One-time setup - installs Playwright in Windows temp directory
powershell.exe -ExecutionPolicy Bypass -File priv/scripts/start_server.ps1 -Install

This creates %TEMP%\playwriter-server on Windows with Playwright installed.

3. Use from Elixir

# Simple fetch
{:ok, html} = Playwriter.fetch_html("https://example.com", mode: :windows)

# Screenshot
{:ok, png} = Playwriter.screenshot("https://example.com", mode: :windows)
File.write!("screenshot.png", png)

# Full browser control
Playwriter.with_browser([mode: :windows], fn ctx ->
  :ok = Playwriter.goto(ctx, "https://example.com")
  :ok = Playwriter.fill(ctx, "input[name=q]", "search term")
  :ok = Playwriter.click(ctx, "button[type=submit]")
  {:ok, html} = Playwriter.content(ctx)
  html
end)

Development Workflow

Interactive Development

Start an IEx session and explore:

iex -S mix

# See the browser while you work
{:ok, result} = Playwriter.with_browser([mode: :windows], fn ctx ->
  :ok = Playwriter.goto(ctx, "https://example.com")

  # Pause here - inspect the browser on Windows
  IO.gets("Press Enter to continue...")

  {:ok, html} = Playwriter.content(ctx)
  html
end)

Debugging Tips

  1. Use :windows mode - See what's happening in real-time
  2. Add pauses - Use Process.sleep/1 or IO.gets/1 to slow down
  3. Take screenshots - Capture state at key points
Playwriter.with_browser([mode: :windows], fn ctx ->
  :ok = Playwriter.goto(ctx, "https://example.com")

  # Screenshot before clicking
  {:ok, before} = Playwriter.screenshot(ctx)
  File.write!("before.png", before)

  :ok = Playwriter.click(ctx, "button.submit")
  Process.sleep(1000)  # Wait for navigation

  # Screenshot after
  {:ok, after_shot} = Playwriter.screenshot(ctx)
  File.write!("after.png", after_shot)
end)

How It Works

The :windows mode uses the Playwriter.Transport.WindowsCmd transport which:

  1. Writes a Node.js script to %TEMP%\playwriter-server\transport.js on Windows
  2. Launches PowerShell with that script via Erlang Ports
  3. Communicates via JSON messages over stdin/stdout
  4. The Node.js script controls Playwright/Chromium on Windows

This approach completely bypasses networking, avoiding all WSL2 Hyper-V firewall issues.

Troubleshooting

"Playwright not installed" or "Cannot find module 'playwright'"

Run the setup script with -Install:

powershell.exe -ExecutionPolicy Bypass -File priv/scripts/start_server.ps1 -Install

Or manually install on Windows:

cd $env:TEMP\playwriter-server
npm install playwright
npx playwright install chromium

"Timeout waiting for transport to start"

Check that:

  1. Node.js is installed on Windows and in PATH
  2. Playwright is installed in %TEMP%\playwriter-server

Test manually:

# From WSL
powershell.exe -Command "cd $env:TEMP\playwriter-server; node -e 'console.log(require(\"playwright\").chromium)'"

"Browser closes immediately"

Make sure you're not letting the session close. Use with_browser to keep the browser open:

Playwriter.with_browser([mode: :windows], fn ctx ->
  :ok = Playwriter.goto(ctx, "https://example.com")
  Process.sleep(5000)  # Keep browser open for 5 seconds
  :ok
end)

"PowerShell execution policy error"

Always run with -ExecutionPolicy Bypass:

powershell.exe -ExecutionPolicy Bypass -File script.ps1

Checking Windows User Detection

The transport detects your Windows username from /mnt/c/Users/. If it picks the wrong user:

# Check what user is detected
File.ls!("/mnt/c/Users")
|> Enum.reject(&(&1 in ["Public", "Default", "Default User", "All Users", "desktop.ini"]))

Comparison: Windows Mode vs Remote Mode

Feature:windows mode:remote mode
Setup requiredJust npm installRun a server
Network issuesNone (stdin/stdout)WSL2 firewall blocks it
PerformanceGoodSlightly faster
WSL2 compatibleYesNo (blocked by Hyper-V)
Recommended for WSLYesNo

Use :windows mode for WSL-to-Windows browser automation. The :remote mode exists for other distributed scenarios but doesn't work reliably from WSL2.

Next Steps