easypresent
v0.1.1
Published
Lightweight, secure, local-network remote-control system. Control your computer from any browser on the same network.
Maintainers
Readme
EasyPresent
Lightweight, secure, local-network remote control. Control your computer from any browser on the same network — no cloud, no app install, no account.
Run one command, scan a QR code with your phone, and you get a live view of your screen — all over your local network.
npm install -g easypresent
easypresent────────────────────────────────────────────────
EasyPresent
Local-network remote control
────────────────────────────────────────────────
Local: http://localhost:3000
Network: http://192.168.1.25:3000
Pair code: AB3XK7
Scan this QR code with your phone:
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
...
Waiting for connection...Quick start
npm install -g easypresent
easypresentOr without installing:
npx easypresentThen:
- Note the Network URL and pair code in the terminal (or scan the QR code).
- Open the URL on your phone/tablet/laptop browser.
- Enter the pair code (scanning the QR code fills it in automatically).
- You're connected — you'll see your host screen live.
How it works
Run EasyPresent → Scan QR → Phone opens browser → Pair with code → See host screen → Control hostEasyPresent runs entirely on your machine. No cloud server. No relay. No account. Nothing leaves your local network.
Supported platforms
| Capability | Windows | macOS | Linux | | ----------------- | :-----: | :---: | :---: | | Screen capture | ✅ | ✅ | ✅ | | Keyboard injection | ✅ | ✅ | ✅ (X11) | | Mouse injection | ✅ | ✅ | ✅ (X11) | | Web UI | ✅ | ✅ | ✅ |
- Windows: works out of the box.
- macOS: requires Accessibility and Screen Recording permissions (see below).
- Linux: works on X11. Wayland support is limited; run the host under XWayland for best results.
OS permissions
macOS
Grant the application running EasyPresent (usually Terminal, iTerm2, or VS Code) the following permissions in System Settings → Privacy & Security:
- Accessibility — required for keyboard and mouse injection.
- Screen Recording — required for screen capture.
After toggling these on, restart the app running EasyPresent.
Windows
No special permissions required. Some antivirus software may flag synthetic input; allow EasyPresent if prompted.
Linux
- X11: works without extra setup for the user's own session.
- Wayland: input injection and screen capture are restricted. Use XWayland or an X session.
CLI options
easypresent [options]
Options:
-p, --port <port> Port to listen on (default: 0 = auto-select)
-H, --host <host> Host/interface to bind (default: 0.0.0.0)
--no-screen Disable screen capture/streaming
--no-input Disable keyboard/mouse input injection
--token <token> Use an explicit pairing token
--max-devices <n> Maximum connected devices (default: 1)
--multi-controller Allow multiple devices to control at once
--fps <n> Target frames per second (default: 30)
--quality <n> JPEG quality 1-100 (default: 70)
--max-width <px> Maximum stream width (default: 1920)
--tls-cert <path> Path to TLS cert (enables HTTPS)
--tls-key <path> Path to TLS key (enables HTTPS)
--tls-ca <path> Optional TLS CA bundle
--allow-origin <o...> Allowed origins (repeatable)
--no-qr Do not print a QR code
-v, --verbose Verbose logging
-h, --help Show helpExamples:
easypresent --port 3000
easypresent --host 0.0.0.0 --fps 60 --quality 85
easypresent --no-screen # input only (great for presentations)
easypresent --no-input # view only
easypresent --token MYCODE # fixed pair code
easypresent --tls-cert cert.pem --tls-key key.pemIf the requested port is taken, EasyPresent automatically picks an available one.
The remote web interface
The UI is mobile-first, dark, and touch-optimized. It shows a live view of your host screen:
- Live view of the host display.
- Scales to your device, supports fullscreen and landscape lock.
- Automatic reconnection and clear connection/stream status (loading, failure, reconnecting).
Programmatic API
import { EasyPresent } from "easypresent";
const remote = new EasyPresent({
port: 3000,
fps: 60,
quality: 80,
// noScreen: false,
// noInput: false,
// maxDevices: 2,
// allowMultipleControllers: false,
});
await remote.start();
remote.on("deviceConnected", (device) => {
console.log("connected:", device.name, device.remoteAddress);
});
remote.on("deviceDisconnected", (device) => console.log("gone:", device.name));
remote.on("key", ({ msg, device }) => console.log("key", msg.key, msg.action, "from", device.name));
remote.on("mouse", ({ msg, device }) => console.log("mouse", msg, "from", device.name));
remote.on("error", (err) => console.error(err));
// Inspect state
console.log(remote.devices); // DeviceInfo[]
console.log(remote.token); // current pairing token
// Manage devices
remote.revokeDevice(deviceId);
remote.rotateToken();
// Stop
await remote.stop();EasyPresentOptions
| Option | Type | Default | Description |
| ------ | ---- | ------- | ----------- |
| port | number | 0 | Port to listen on; 0 auto-selects. |
| host | string | 0.0.0.0 | Interface to bind. |
| noScreen | boolean | false | Disable screen capture/streaming. |
| noInput | boolean | false | Disable input injection. |
| verbose | boolean | false | Verbose logging. |
| token | string | auto | Explicit pairing token. |
| maxDevices | number | 1 | Max concurrent devices. |
| allowMultipleControllers | boolean | false | Let multiple devices control. |
| fps | number | 30 | Target stream FPS. |
| quality | number | 70 | JPEG quality 1–100. |
| maxWidth | number | 1920 | Max stream width. |
| tls | object | — | { cert, key, ca? } to enable HTTPS. |
| allowedOrigins | string[] | [] | Origin allow-list. |
| noQr | boolean | false | Suppress QR code in CLI. |
| webRoot | string | bundled | Path to web UI assets. |
Events
| Event | Payload | Description |
| ----- | ------- | ----------- |
| started | { port, host, url } | Server is listening. |
| stopped | — | Server stopped. |
| deviceConnected | DeviceInfo | A device paired. |
| deviceDisconnected | DeviceInfo | A device left or was revoked. |
| key | { msg, device } | A key event was received. |
| text | { msg, device } | A text event was received. |
| mouse | { msg, device } | A mouse event was received. |
| stream | { msg, device } | A stream control message. |
| error | Error | A non-fatal error. |
Security
EasyPresent is designed for trusted local networks:
- Pairing token — a short, per-session code is required before any control is accepted.
- No unauthenticated access — connections that don't authenticate within 10 seconds are dropped.
- Single controller by default — only one device may inject input; additional devices can observe. Use
--multi-controllerto override. - Device limits —
--max-devicescaps concurrent connections. - Strict message validation — unknown message types and fields are rejected/stripped.
- Rate limiting — per-connection token buckets prevent flooding.
- Origin validation — optional
--allow-originallow-list. - No remote shell — there is no command execution endpoint.
- Optional HTTPS —
--tls-cert/--tls-keyfor encrypted LAN transport. - No cloud — nothing is sent to any external server.
Troubleshooting
No LAN address detected. Make sure you're connected to Wi-Fi/Ethernet. You can still connect via http://localhost:<port> on the host.
Port X in use. EasyPresent auto-selects another port. Use --port 0 to always auto-select.
macOS: keys/mouse don't do anything. Grant Accessibility permission to the app running EasyPresent and restart it.
macOS: screen is black/blank. Grant Screen Recording permission and restart the app.
Linux/Wayland: input or capture fails. Run under XWayland or an X11 session.
Device can't connect. Confirm the phone is on the same Wi-Fi as the computer and that the network allows local device-to-device traffic (some guest/isolated networks block it).
Verbose logs / stack traces. Run with --verbose or set EASYPRESENT_DEBUG=1 for a full stack trace on startup failures.
License
MIT — see LICENSE.
