| android | ||
| backend | ||
| ios | ||
| lib | ||
| linux | ||
| macos | ||
| nginx | ||
| test | ||
| web | ||
| windows | ||
| .gitignore | ||
| .metadata | ||
| analysis_options.yaml | ||
| docker-compose.yml | ||
| pubspec.lock | ||
| pubspec.yaml | ||
| README.md | ||
Pulsar
A LAN remote desktop app built with Flutter + WebRTC.
The host shares their screen; the viewer streams it and forwards mouse/keyboard input back.
Architecture
Host (desktop) <──WebRTC──> Viewer (any platform)
│ │
└────── WebSocket ───────────┘
│
Signalling Server
(Node.js / ws)
- Signalling server — relays WebRTC offer/answer/ICE between peers. In-memory, no auth.
- Host — captures screen with
getDisplayMedia, streams it via WebRTC. Desktop/Web only. - Viewer — receives the video stream, renders it full-screen, and sends input events back over a WebRTC data channel.
- Input injection — host injects received events into the OS via platform channels (CGEvent on macOS, SendInput on Windows, XTest on Linux/X11).
Running the backend
cd backend
npm install
npm start
The server listens on ws://localhost:3000 by default.
Running the Flutter app
flutter pub get
flutter run -d macos # or windows, linux
macOS notes
Screen capture requires granting Screen Recording permission in
System Settings → Privacy & Security → Screen Recording.
Input injection requires granting Accessibility permission in
System Settings → Privacy & Security → Accessibility.
The sandbox is disabled in debug/release entitlements so that CGEvent can post
events to other processes. Do not ship this as-is to the Mac App Store.
Windows notes
No extra permissions needed. SendInput works out of the box.
Linux notes
Only X11 is supported for input injection. Wayland sessions will not receive
injected events. Make sure libxtst-dev is installed before building:
sudo apt install libxtst-dev # Debian / Ubuntu
sudo dnf install libXtst-devel # Fedora
Usage
- Start the signalling server.
- On the host machine: open Pulsar → Host. A 6-character room code appears.
- On the viewer machine: open Pulsar → Connect, enter the room code.
- The viewer will display the host's screen. Move the mouse and type to control the host.
Both machines must be able to reach the signalling server. For LAN testing, use the
host's local IP in the server URL field (e.g. ws://192.168.1.10:3000).
Key input mapping caveat
Flutter's logical key IDs are not the same as OS virtual key codes. The current
implementation uses the low byte of the logical key ID as a rough approximation.
This works for standard ASCII keys but may misbehave for function keys, modifiers,
and non-Latin layouts. A proper mapping table would be needed for production use.