-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
Electron + Playwright — Deep Dive Analysis
Overview
Electron provides a Chromium-based desktop shell. Playwright runs as a Node.js library for browser automation. Combined, you get a desktop app with powerful browser control.
Architecture
┌──────────────────────────────┐
│ Electron Main Process │
│ ┌────────────────────────┐ │
│ │ Task Scheduler │ │
│ │ (BullMQ / custom) │ │
│ ├────────────────────────┤ │
│ │ Playwright Engine │ │
│ │ - 1 Browser instance │ │
│ │ - 100 BrowserContexts│ │
│ │ - Per-context proxy │ │
│ ├────────────────────────┤ │
│ │ IPC Bridge │ │
│ └────────────────────────┘ │
├──────────────────────────────┤
│ Electron Renderer │
│ ┌────────────────────────┐ │
│ │ React/Vue Dashboard │ │
│ │ - Task list & status │ │
│ │ - Live preview pane │ │
│ │ - Config panel │ │
│ │ - Logs viewer │ │
│ └────────────────────────┘ │
└──────────────────────────────┘
Key Dependencies
{
"electron": "^33.x",
"playwright-core": "^1.49.x",
"bullmq": "^5.x",
"better-sqlite3": "^11.x",
"react": "^19.x",
"electron-builder": "^25.x"
}Proxy per Task Implementation
const browser = await chromium.launch();
// Each task gets its own isolated context with unique proxy
async function createTask(proxyConfig: ProxyConfig) {
const context = await browser.newContext({
proxy: {
server: proxyConfig.server, // e.g., "http://proxy-us-east:8080"
username: proxyConfig.username,
password: proxyConfig.password,
},
locale: proxyConfig.locale, // e.g., "en-US"
timezoneId: proxyConfig.timezone, // e.g., "America/New_York"
});
const page = await context.newPage();
return { context, page };
}Concurrency Model
import { Worker } from 'worker_threads';
// Main process spawns worker threads for CPU-bound tasks
// Playwright contexts share single browser (memory efficient)
const CONCURRENCY_LIMIT = 100;
const semaphore = new Semaphore(CONCURRENCY_LIMIT);
async function runTask(taskConfig: TaskConfig) {
await semaphore.acquire();
try {
const { context, page } = await createTask(taskConfig.proxy);
await executeSteps(page, taskConfig.steps);
await context.close();
} finally {
semaphore.release();
}
}Strengths
- Rich Playwright API: Auto-wait, smart selectors, network interception
- Multi-browser: Test on Chromium, Firefox, WebKit
- Mature ecosystem: electron-builder, auto-update, crash reporting
- Context isolation: Each task fully isolated (cookies, storage, proxy)
- Screenshot/video: Built-in recording per context
- Familiar stack: JavaScript/TypeScript throughout
Weaknesses
- Heavy bundle: ~200MB+ (ships Chromium twice — Electron + Playwright)
- RAM hungry: ~3-4GB for 100 contexts + Electron overhead
- Single-threaded gotcha: Main process can bottleneck if not using workers
- Slow startup: ~3s to launch
- Security surface: Chromium vulnerabilities affect app
Resource Estimates (100 tasks)
| Resource | Estimate |
|---|---|
| RAM | 3-4 GB |
| CPU | 4-8 cores recommended |
| Disk | ~500MB installed |
| Network | Depends on tasks |
| Startup | ~3s app + ~2s browser |
When to Choose This Stack
✅ You want a desktop app with native feel
✅ You need multi-browser testing
✅ Your team knows JavaScript/TypeScript
✅ You need rich browser automation (auto-wait, selectors, interception)
✅ Task count stays under ~150 concurrent
❌ Avoid if: you need >200 concurrent tasks, bundle size matters, or server deployment
Verdict: 6.65/10
Good feature set but heavy resource usage and large bundle. Better options exist for pure scale.
References issue #1 for full comparison
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels