🌐 Website • 🚀 Quick Start • ⚙️ How It Works • 🔒 Tunnels
The host machine runs a Python server and any browser on any device becomes the input + display client. Built-in tunnel support (Cloudflare, Tailscale, Pinggy, Zrok2) handles NAT/firewall traversal without touching your router.
PyPI: https://pypi.org/project/anywhereinput/
pip install anywhereinput
anywhereinput --tunnel cloudflarePaste the printed URL into any browser on another device. That's it - your screen shows up in the tab, fully controllable with mouse and keyboard.
pip install anywhereinput[app]
anywhereinput --appOpens a PyQt6 window for token management and server controls.
sudo apt install pipx && pipx ensurepath
pipx install anywhereinput
anywhereinput --tunnel cloudflareUse the scripts in scripts/windows/ (.bat) or scripts/unix/ (.sh). They handle dependency setup and launch.
- Screen capture via
mss+ Pillow, JPEG stream at configurable FPS up to 120 - Mouse + keyboard input forwarded from browser WebSocket back to the host OS via pyautogui
- Tunnel providers: Cloudflare, Tailscale, Pinggy, Zrok2 - spawns the binary and negotiates the tunnel automatically
- Local-only mode for LAN use (no internet required)
- Multi-monitor support (
--monitor N) - Per-launch 32-char auth tokens, IP allow/block lists, per-token permissions
- Audit logging (JSONL), rate limiting, structured log rotation
pip install anywhereinput # core server + client UI
pip install anywhereinput[app] # + optional PyQt6 admin appSystem requirements: Python 3.9+, any OS with a display server (Wayland/X11 on Linux, regular desktop on Windows/macOS). Client side just needs a browser that supports WebSocket + Pointer Events (basically anything modern).
Runtime deps: pyautogui, aiohttp, requests, Pillow, mss, pyyaml. Optional for GUI: PyQt6.
anywhereinput --tunnel local # same network, no tunnel
anywhereinput --tunnel cloudflare # auto Cloudflare tunnel (no account needed)
anywhereinput --tunnel tailscale # Tailscale peer-to-peer
anywhereinput --tunnel pinggy # SSH-based tunnel
anywhereinput --tunnel zrok2 # Zrok2 open-source tunnel
# CLI flags override config file values:
anywhereinput --fps 60 --quality 70 --scale 0.8 --tunnel cloudflareTunnel selection prompts interactively if you just run anywhereinput with no flags (in a terminal). Runs non-interactively as Cloudflare tunnel if there's no TTY.
Config loads from YAML at startup. CLI flags take priority.
anywhereinput config init # create defaults
anywhereinput config edit # open in $EDITORUser overrides go in config/local_settings.yaml (gitignored). Project defaults are in config/settings.yaml.
See docs/USAGE.md for the full config guide.
| Flag | Default | Description |
|---|---|---|
--host HOST |
127.0.0.1 | Bind address |
--port PORT |
8008 | Server port |
--fps FPS |
120 | Capture FPS (1-120) |
--quality Q |
40 | JPEG quality (1-95). Lower = faster encode, blurrier image |
--scale F |
0.7 | Scale factor for capture (0.1-1.0) |
--low-bandwidth |
- | Mobile preset: 15fps, q60, half scale |
--no-capture |
- | Disable screen capture |
--monitor N |
0 | Monitor index (0=auto) |
--tunnel P |
interactive | Provider: cloudflare, tailscale, pinggy, zrok2, local |
-v / --verbose |
- | DEBUG logging (-vv for extra) |
--quiet |
- | Console silent, write to file only |
--log-level L |
INFO | Log level |
--version |
- | Show version |
- Auto-generated 32-char token per launch (fresh every restart)
- Per-token permissions: move, click, scroll, keyboard, screen_toggle, ping
- IP allowlist + block list (CIDR or single-host), per token and global
- Kick connected clients + add to block list
- Rate limiting on WebSocket auth, API endpoints, and token creation
- Audit trail in JSONL log for all security events
- HTTPS/WSS via tunnel providers
- No external data storage
For enterprise needs (OAuth, mTLS, SSO), put it behind a reverse proxy like Caddy or Nginx.
Browser (any device) <==WebSocket==> Python server (your PC)
├── aiohttp HTTP + WS server
├── mss/PIL screen capture
└── pyautogui input forwarding
[optional tunnel]
Cloudflare / Tailscale / Pinggy / Zrok2
- Pick a tunnel (or use local)
- Run the server on your PC
- Open the URL in any browser
- Enter the token → connect
- Control your PC
{"type": "auth", "token": "***"}{"type": "move", "mode": "relative", "dx": 10, "dy": 15}
{"type": "move", "mode": "absolute", "dx": 0.5, "dy": 0.5}
{"type": "click", "button": "left", "clicks": 1}
{"type": "scroll", "amount": 15}
{"type": "key", "key": "enter"}
{"type": "hotkey", "keys": ["ctrl", "c"]}
{"type": "screen_toggle", "enabled": true}{"type": "screen", "data": "<base64-jpeg>"}
{"type": "screen_status", "status": "rebuilding", "message": "Reconnecting..."}
{"error": "capture_error", "message": "Input engine recovering.", "recovering": true}| Endpoint | Description |
|---|---|
GET /health |
Uptime, client count, screen state |
GET /api/screen |
Screen dimensions |
GET /api/engine |
Input engine health + live FPS/bandwidth metrics |
GET /api/monitors |
All monitors + current selection |
POST /api/monitor/{index} |
Switch capture monitor |
GET /api/tokens |
List tokens (masked) |
POST /api/tokens |
Create token with permissions |
PATCH /api/tokens/{token} |
Update token |
DELETE /api/tokens/{token} |
Revoke token |
GET /api/clients |
Connected clients |
POST /api/clients/{id}/kick |
Kick client + block IP |
pip install -e ".[dev]"
python -m pytest tests/ -v
# Individual suites:
python -m pytest tests/test_config_loader.py -v
python -m pytest tests/test_auth.py -v
python -m pytest tests/ws_test.py -vCode quality checks: black --check src/, flake8 src/, mypy src/anywhereinput.
| Problem | Fix |
|---|---|
| Config not loading | Check YAML syntax |
| Port in use | Use --port or check with lsof -i :8008 |
| Tunnel URL not showing | Check internet connection; try a different provider |
| Can't connect from device | Check firewall (port 8008); test localhost first |
| Mouse lag | Reduce quality/scale, try LAN instead of tunnel |
| Black screen | Install mss; ensure display server is available on Linux |
| Blank stream | Check server logs; try --no-capture to isolate the issue |
| Keyboard double-typing | Check autocorrect in the browser; 50ms debounce is active |
| Cloudflare binary missing | Auto-downloads on first run, or install manually |
| Zrok2 "not enabled" | Run zrok2 enable <TOKEN> or use zrok2_repair.py |
- Rate limiting (per-IP: WebSocket auth, API, token creation)
- Audit logging (JSONL rotating log)
- Capture mode presets + custom modes in admin app
- Structured logging with rotating file handler
--low-bandwidthflag- Adaptive streaming (per-client backpressure, auto-FPS)
- Global IP blocking via TokenManager
- 328 tests covering the above
- Zero-trust token startup (clears all tokens on start)
- Kick action separated from global IP block
- Tombstone token rejection for revoked tokens
- File transfer over tunnel
- Audio stream
- Docker support for headless deployment
- Enterprise SSO integration
This solves a different problem than most remote desktop apps.
AnywhereInput wins when you need:
- Browser-only client (no install on the controlling device)
- No accounts or signup walls
- Access from behind strict NAT/firewall without port config
- Quick access ("poke at my desktop for 5 minutes")
- Works on phones, tablets, public computers
Other tools are better when you need:
- Lower latency / optimized video codecs (Sunshine/Moonlight/Parsec)
- Full remote desktop workflows with file transfer and session recording (TeamViewer, RustDesk)
- Audio forwarding (all of the above except AnywhereInput currently)
- Long sessions across multiple monitors at native quality
TL;DR: it's not a replacement for RDP or TeamViewer. It's the tool you reach for when those are too heavy for what you need.
See CONTRIBUTING.md.
MIT - see LICENSE
