
Have you ever been lying on the couch watching a movie on your PC, only to realize you need to get up just to adjust the volume or skip an intro?
For years, I used classic remote apps like Unified Remote or Remote Mouse. But over time, they either became bloated, loaded with ads, or locked simple features behind paid subscriptions. More importantly, many modern tools route commands through third-party cloud servers just to send a keystroke across a living room.
I wanted something lightweight, blazingly fast, and strictly local-first (no external servers, no account signups, zero telemetry tracking keystrokes).
So over the last few months, I built Pocket Remote. In this post, I want to break down the architectural decisions, the low-latency networking challenges, and how we handle local pairing securely.
šļø High-Level Architecture: The Monorepo Structure
To keep the mobile client and desktop receiver perfectly synchronized, the project is structured as a TypeScript monorepo:
āāā apps/
ā āāā mobile/ # React Native / Expo (Android client)
ā āāā receiver/ # Electron + Node.js (Windows background tray app)
āāā packages/
āāā protocol/ # Shared TypeScript types & Zod schemas
Why a Shared Protocol Package?
When dealing with real-time remote commands (cursor deltas, media hotkeys, file chunks), schema mismatches between client and server are fatal.
By having packages/protocol, both the mobile app and desktop receiver import the exact same TypeScript interfaces and Zod validation schemas. If the payload format changes during development, type-checking breaks immediately across the entire workspace.
// packages/protocol/src/commands.ts
import { z } from 'zod';
export const MouseMoveSchema = z.object({
type: z.literal('mouse.move'),
dx: z.number(),
dy: z.number(),
timestamp: z.number(),
});
export type MouseMoveCommand = z.infer<typeof MouseMoveSchema>;
ā” The Networking Layer: Why WebSockets over Local LAN?
When building a remote mouse and keyboard, the biggest enemy is perceived input lag. If the cursor moves even 50ms after your finger touches the glass, the experience feels sluggish.
Why not WebRTC or UDP?
While pure UDP is standard for gaming, managing raw UDP sockets directly inside cross-platform mobile environments without native custom bridging can be unnecessarily complex.
WebRTC requires a signaling server and complex ICE/STUN handshakes ā overkill for two devices already sitting on the exact same Wi-Fi subnet.
The WebSocket Sweet Spot:
We opted for a lightweight local WebSocket server hosted directly by the Electron process (ws library in Node.js).
Because the packets travel only through the local Wi-Fi router (typically < 3ms round-trip), TCP overhead is practically negligible, while giving us reliable delivery for critical actions like file chunks, clipboard text, and macros.
š¤ Zero-Friction QR Pairing & Handshake Protocol
One of the biggest friction points in LAN utilities is manual IP configuration (192.168.1.xxx:port). Most users don't know their local IP address, and mDNS/Bonjour discovery is notoriously unreliable across various Windows Firewall configurations.
How Pocket Remote Solves This:
[Windows Receiver] [Mobile Client]
ā ā
āā Generate Local IP + Ephemeral Token ā
āā Render QR Code on Screen ā
ā ā
ā 1. Scan QR Code ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā> ā
ā ā
ā 2. ws://<LAN_IP>:<PORT> ā
ā <āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
ā 3. pair.request (Token) ā
ā <āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
āā Prompt User on Windows: "Approve Device?"
ā ā
ā 4. pair.approved (Auth Key) ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā> ā
The Windows receiver determines its primary network interface IP and spins up a local WebSocket port.
It displays a QR code containing {"ip": "...", "port": ..., "pairingKey": "..."}.
Mobile scans the QR, connects instantly to the local IP, and initiates a handshake.
The Windows receiver prompts a native notification: "Allow device 'Pixel 8' to connect?".
Once approved, an auth token is exchanged and securely stored in AsyncStorage on mobile. Subsequent connections reconnect automatically without scanning again.
š±ļø Optimizing Touchpad Latency & Delta Movement
Handling touchpad gestures requires a balance between fluidity and network congestion.
Mobile touchscreens report touch events at 60Hz or 120Hz. If you fire a WebSocket frame on every single micro-movement event, you flood the network buffer and introduce packet queueing latency.
The Solution: Delta Batching & Smoothing
Instead of sending absolute coordinates, the mobile app calculates relative deltas (dx, dy).
// Simplified Touch Handler
const onTouchMove = (event: GestureEvent) => {
const dx = event.translationX - lastX.current;
const dy = event.translationY - lastY.current;
lastX.current = event.translationX;
lastY.current = event.translationY;
// Apply natural curve & velocity multiplier
sendThrottledCommand({
type: 'mouse.move',
dx: Math.round(dx * sensitivity),
dy: Math.round(dy * sensitivity),
timestamp: Date.now()
});
};
On the Windows side, the receiver translates these deltas directly into OS cursor movement using native Win32 input APIs, providing sub-10ms response times that feel indistinguishable from a physical trackpad.
š ļø Beyond the Mouse: LAN File Drop, Clipboard & Macros
Once the reliable local communication pipeline was built, adding power-user productivity features was a natural evolution:
Instant Clipboard Sync: Copy an OTP or link on mobile, hit one button, and it pastes straight at the active PC cursor.
Fast LAN File Transfer: Stream files chunk-by-chunk directly into the user's Downloads folder at local Wi-Fi speeds without cloud file size limits.
Custom Macros: Trigger multi-key combinations (e.g., Win+D, Alt+F4, task switching) with single-tap quick buttons.
š” What I Learned from Building Local-First
Zero-Cloud Architecture is Liberating: There are no AWS server bills, no Redis caches to manage, and no database migrations to worry about. The user's hardware does all the work.
Privacy as a Feature: In an era where every utility app demands an email and tracks user telemetry, users genuinely appreciate software that requires no account and keeps all data within their home network.
TypeScript Monorepos are Essential: Sharing schemas between mobile and desktop saved dozens of debugging hours.
š Try It Out & Share Your Thoughts!
Pocket Remote is currently free and available for Windows & Android: š Website / Downloads: pocketremote.cloud
I'm actively working on the next roadmap milestones (including an upcoming on-device Camera OCR to PC cursor feature).
Iād love to hear your thoughts on the architecture:
What challenges have you faced when bridging Mobile and Desktop over local networks?
Would you prefer WebSockets or WebRTC data channels for this kind of local tooling?
Feel free to ask technical questions in the comments!
Top comments (0)