# PixelStream πŸ–₯️➑️🌐 A mini "TeamViewer for a browser". You open a web UI, hit **Start Browser**, and a **headless Chromium running inside a Docker container** streams its screen back to your browser in real time. You can **click, scroll, type, and navigate** β€” and it all happens inside that remote headless browser........... --- ## How it works ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” click / scroll / type (WebSocket) β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” CDP β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Web UI β”‚ ───────────────────────────────────► β”‚ Node backend β”‚ ───────► β”‚ Headless Chromium β”‚ β”‚ (Next.js) β”‚ β”‚ (ws + CDP) β”‚ β”‚ (Puppeteer) β”‚ β”‚ β”‚ ◄─────────────────────────────────── β”‚ β”‚ ◄─────── β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ live JPEG frames (WebSocket) β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ screencast β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` - **Frontend** (`/frontend`) β€” Next.js app. A `` paints the live frames; mouse/keyboard/scroll events are captured, converted to real browser coordinates, and sent over a WebSocket. - **Backend** (`/backend`) β€” Node.js + `ws` + Puppeteer. Launches Chromium, uses the **Chrome DevTools Protocol (CDP)** `Page.startScreencast` to get a continuous stream of JPEG frames, and injects input via `Input.dispatchMouseEvent` / `Input.dispatchKeyEvent`. - **Docker** β€” both services run in containers via `docker compose`. The backend uses the official Puppeteer image (Chromium + system deps preinstalled). ### Why CDP screencast (not a screenshot loop)? `Page.startScreencast` only emits a frame when the page actually changes, and it's much smoother and lower-latency than taking `page.screenshot()` on a timer. --- ## Run it ### Option A β€” Docker (recommended) ```bash docker compose up --build ``` Then open **http://localhost:3000** and click **Start Browser**. ### Option B β€” Local (no Docker, for fast iteration) ```bash # terminal 1 cd backend && npm install && npm start # ws server on :8080 # terminal 2 cd frontend && npm install && npm run dev # UI on :3000 ``` --- ## Deploy (Frontend β†’ Vercel, Backend β†’ Render) > ⚠️ The browser page is served over **HTTPS** (Vercel), so it can only open a > **secure** WebSocket (`wss://`). Render gives you an `https://…onrender.com` > URL, whose WebSocket endpoint is `wss://…onrender.com`. Plain `ws://` will be > blocked as mixed content. ### 1. Backend on Render - New **Web Service** β†’ connect this repo. - Runtime: **Docker**, Dockerfile path: `backend/Dockerfile`, context: `backend` (or just use the included `render.yaml` Blueprint). - Deploy. Note the URL, e.g. `https://pixelstream-backend.onrender.com`. - Render injects `PORT` automatically β€” the server already reads it. ### 2. Frontend on Vercel - New Project β†’ import this repo β†’ set **Root Directory = `frontend`**. - Add an environment variable: | Key | Value | |-----|-------| | `NEXT_PUBLIC_WS_URL` | `wss://pixelstream-backend.onrender.com` | - Deploy. Open the Vercel URL β†’ **Start Browser**. --- ## What works βœ… - Start a headless Chromium in a Docker container from the web UI - Live screen streaming via CDP screencast (JPEG frames over WebSocket) - Mouse: move, left/right click, scroll - Keyboard: printable characters + common special keys (Enter, Backspace, Tab, arrows, Esc, Delete…) - URL bar to navigate the remote browser - Accurate click mapping (canvas coords β†’ real viewport coords) ## Known limitations / where it gets hard ⚠️ - **Keyboard coverage** β€” only common special keys are mapped; modifier combos (Ctrl+C, Shift+selection) aren't fully wired yet. - **Single session** β€” one browser per server; no multi-user / multi-tab. - **Streaming** β€” JPEG-over-WebSocket is simple but bandwidth-heavy; WebRTC or VP8/H.264 encoding would be smoother at scale. - **Chromium-in-Docker** β€” needs `--no-sandbox`, `--disable-dev-shm-usage`, and `shm_size: 1gb` or Chromium crashes. (These are already set.) ## Next steps πŸš€ 1. Full keyboard model with modifiers + IME/composition events 2. Multiple isolated sessions (one container per user, spawned on demand) 3. WebRTC streaming for lower latency / adaptive quality 4. Reconnect handling + session timeouts + resource cleanup --- ## Project structure ``` Pixel/ β”œβ”€β”€ docker-compose.yml β”œβ”€β”€ backend/ β”‚ β”œβ”€β”€ Dockerfile β”‚ β”œβ”€β”€ package.json β”‚ └── server.js # ws server + Puppeteer + CDP screencast/input └── frontend/ β”œβ”€β”€ Dockerfile β”œβ”€β”€ package.json β”œβ”€β”€ next.config.js └── app/ β”œβ”€β”€ layout.js └── page.js # UI + canvas + input capture ```