Consent-first remote desktop, open source. A host shares its screen over WebRTC; a viewer in any browser sees it live and controls the host's real mouse and keyboard. AnyDesk-style, ~1000 lines, no accounts, no cloud.
HOST (Electron) SIGNALING (Node+ws) VIEWER (browser)
├─ desktopCapturer ─┐ ┌─ <video> fullscreen
│ ├── WebRTC video ─── P2P (STUN/TURN) ────┤
│ consent prompt │ relay SDP/ICE via signaling only │ capture mouse/kbd
└─ input-worker ◄───┴── WebRTC data channel ◄────────────────┘ send input events
(Node + nut-js)
injects real OS input
The classic pain of native modules: nut-js ships a prebuilt binary for the
standard Node ABI, not Electron's. Instead of running electron-rebuild on every
install, BeamDesk runs OS input injection in a separate plain-Node child
process (worker/input-worker.js) that the Electron host spawns. Electron does
what it's best at (hardware-accelerated screen capture); system Node does what
nut-js needs (a matching ABI). No native rebuild step, ever.
The signaling server never sees pixels or keystrokes — media and control travel peer-to-peer once the two sides find each other.
| Path | Runtime | Job |
|---|---|---|
server/signal.js |
Node | Serves the viewer, relays SDP/ICE, owns codes |
viewer/ (index/js/css) |
Any browser | Answers the offer, shows screen, sends input |
host/ (Electron) |
Electron | Captures screen, consent UI, makes the offer |
worker/input-worker.js |
System Node | Injects mouse/keyboard via nut-js |
npm install1. Start the signaling server (also serves the viewer page):
npm run signal2. Start the host (the machine to be controlled):
npm run hostClick Start sharing — a 6-digit code appears.
3. Open the viewer in a browser: http://localhost:8080 Enter the code, click Connect. The host gets an Accept / Reject prompt. Accept, and you're driving the host.
To point the host at a signaling server on another machine, set the field in
the host window, or launch with BEAMDESK_SIGNAL=ws://SERVER_IP:8080 npm run host.
- Mouse — move, click, right-click, drag, scroll all forward to the host.
- Keyboard — every key is sent to the host while control is on (so
ffullscreens YouTube there, Alt+Tab switches windows there, etc.). - ⛶ Fullscreen — view the remote edge-to-edge.
- control toggle — flip to view-only without disconnecting.
This is a remote-support tool, by design:
- The host must click Start sharing, then explicitly Accept each viewer.
- The session code is short-lived and single-use (one viewer per host).
- Nothing is captured or injected until both steps happen.
Don't use it to access a machine whose owner hasn't agreed. That's the whole point of the accept prompt.
- Single monitor. Capture + input mapping assume the primary display.
- NAT. STUN handles most networks; hosts behind strict/symmetric NAT need a
TURN server — add one to
RTC_CONFIGinviewer/viewer.jsandhost/host.js. - No encryption beyond WebRTC's (DTLS-SRTP, which is mandatory) and the
signaling channel (use
wss://behind a TLS reverse proxy in production). - No file transfer / clipboard sync yet. Natural next features.
npm run distOutputs to dist/:
BeamDesk-<version>-x64.exe— NSIS installer (choose install dir, start-menu shortcut)BeamDesk-<version>-portable.exe— single-file, no install
The app has no external Node dependency: input injection runs in an Electron
utilityProcess, and nut-js's N-API prebuild loads under Electron's ABI. The
native binary is asarUnpacked so it can be dlopen'd at runtime.
electron-builder eagerly extracts its winCodeSign bundle, which contains macOS
.dylib symlinks. Creating symlinks on Windows needs Developer Mode or an
elevated shell, so extraction fails with "a required privilege is not held by
the client" — even though those files are useless for a Windows build.
Fix once by pre-extracting the bundle (the 2 symlink errors are harmless):
CACHE="$LOCALAPPDATA/electron-builder/Cache/winCodeSign"
"node_modules/7zip-bin/win/x64/7za.exe" x "$CACHE"/*.7z -o"$CACHE/winCodeSign-2.6.0" -y
rm -rf "$CACHE"/[0-9]*Then npm run dist succeeds. (Or just enable Windows Developer Mode / run the
build from an elevated terminal.)
Builds are unsigned — Windows SmartScreen will warn on first run. Add a real code-signing cert via
CSC_LINK/CSC_KEY_PASSWORDfor distribution.
MIT © larprober