Zero cloud · zero accounts · your own Wi-Fi

Your phone is the remote for your Windows & Linux laptop.

Launcher, touchpad, keyboard, a live screen you can tap, media & volume, brightness, and lock / sleep / shutdown — all from the phone in your hand. One Node.js process on the laptop, a PWA on the phone. Nothing leaves your network.

Windows & Linux Node.js ≥ 20 Installable PWA Works over Tailscale

Phone

Browser or installed PWA

LapDeck agent

One Node.js process on Windows or Linux

In your hand

The whole laptop, on a phone screen.

Native-feeling, thumb-friendly, and built as a real PWA — add it to your home screen and it opens like an app.

LapDeck app launcher screen on a phone
Launcher
LapDeck touchpad screen on a phone
Touchpad
LapDeck keyboard with custom shortcuts on a phone
Keyboard & shortcuts
LapDeck settings screen on a phone
Settings
Features

Everything you'd reach for, one tap away.

Seven built-in decks. Each feature is a server-enforced switch — turn any of them off and the agent flat-out refuses those commands.

App launcher

Tiles for apps, folders, files and websites. Add or remove tiles right from the phone, and choose which browser opens a given site — bare names like chrome.exe resolve just like the Run dialog.

Touchpad

Relative pointer with acceleration, tap-to-click, two-finger scroll, two-finger tap for right-click, double-tap-drag, and a speed slider.

Keyboard

Type live into the laptop with IME-safe diffing (swipe-typing works), common shortcuts, and your own chord buttons like ctrl+shift+p.

Live screen view

An MJPEG stream you can tap to click where you tap, with a realtime client-predicted cursor overlay, a magnifier loupe, and low / medium / high quality presets.

Media & system

Play / pause / next / prev, volume, mute, screen brightness and a battery readout — the transport keys your OS already understands.

Power

Lock, sleep, restart, shutdown — each with a confirm and a configurable abort window so one tap can cancel.

Anywhere, not just home

Add free Tailscale and control your laptop from across the world — no port forwarding. The agent detects it and shows a remote QR automatically.

Private by design

Everything is LAN-local. A constant-time token guards the socket, and it rides in the QR so you scan exactly once.

Quick start

Running in under a minute.

Requirements: Windows 10/11 or Linux, Node.js ≥ 20, and your phone on the same Wi-Fi.

1

Clone & install

Grab the repo and pull the dependencies. One command starts the agent.

Terminal
git clone https://github.com/ronak-create/LapDeck.git
cd LapDeck
npm install
npm start
2

Scan the QR code

The terminal prints a QR. Scan it with your phone — the UI opens already paired. The pairing token rides in the link and is stored on the phone, so you only ever scan once.

Phone can't connect? On Windows, allow Node.js through Windows Firewall on private networks (the prompt appears on first run). On Linux, allow the port: sudo ufw allow 8765/tcp. That's the #1 cause.
3

Add to home screen

Open your phone browser's menu → Add to Home screen. LapDeck installs as a PWA and launches full-screen, just like a native app.

4

(Optional) Start automatically at login

Drop a tiny hidden launcher into your Startup folder — no admin, no console window. Autostart runs after you log in, because input injection and screen capture need your interactive session.

PowerShell
powershell -ExecutionPolicy Bypass -File scripts\install-autostart.ps1

Manage it with scripts\stop-agent.ps1 and scripts\uninstall-autostart.ps1.

Anywhere in the world

Control your laptop over Tailscale.

LapDeck is LAN-local by default — but add Tailscale, a free zero-config mesh VPN, and your phone reaches the laptop from anywhere: another Wi-Fi, cellular, the other side of the planet. No port forwarding, no router config, no public exposure. Traffic is encrypted end-to-end between your own two devices.

Once both devices are on your tailnet, start the agent as usual — it detects Tailscale and prints a second “remote” QR / URL automatically, right next to the LAN one. Scan whichever fits where you are.

Get Tailscale (free)
1

Install on the laptop

Install Tailscale for Windows or Linux and sign in with Google, GitHub or Microsoft — one account, no server to run.

2

Install on the phone

Add the Tailscale app and sign in with the same account. Both devices join your private tailnet and get stable 100.x.x.x addresses.

3

Start LapDeck

Run npm start. The agent spots the Tailscale interface and shows a remote QR / URL alongside the local one — scan it while you're away.

4

(Optional) Lock it to Tailscale only

Bind the agent to just your Tailscale IP so it never listens on public Wi-Fi. Set it in settings.json or via env var:

PowerShell — bind to the Tailscale interface
$env:LC_BIND="100.x.y.z"; npm start
Using it

How each deck works.

Once you're paired, every screen is driven from the phone. Here's the day-to-day.

Launcher

Tap a tile to open an app, folder, file or website on the laptop.

  • Tap to add a tile; long-press a tile to remove it.
  • Choose the tile kind: exec, folder, file or url.
  • For a URL tile you can force a specific browser and set a custom icon.

Touchpad

A relative pointer surface — move like a laptop trackpad.

  • Tap = left click · two-finger tap = right click.
  • Two-finger drag = scroll · double-tap-drag = click-and-drag.
  • Tune pointer speed with the slider (stored per phone).

Keyboard

Type straight into the laptop; the diffing is IME-safe so swipe-typing works.

  • Fire built-in common shortcuts with a tap.
  • Add custom buttons for any chord (ctrl+shift+p) or sequence (ctrl+k,ctrl+o).
  • Up to 30 custom shortcut buttons.

Live screen & media

See the primary display and act on it directly.

  • Tap the stream to click at that exact spot; a loupe helps with precision.
  • Switch quality presets (fps / width / JPEG quality) to suit your link.
  • Media transport, volume, mute, brightness, and battery live on the media deck.
Configure

Tune everything — from the phone or a file.

Most settings live on the ⚙ Settings screen. The rest sit in data/settings.json, deep-merged over the defaults — you only write the keys you change. Invalid values are clamped back into range on load.

Environment variables

Set before npm start to override server settings.

LC_PORTListen port; overrides settings.port.
LC_BINDBind address; e.g. a Tailscale IP to listen on that interface only.
LC_DATA_DIRRelocate the whole data/ directory.

Good to know

  • data/ is created on first run and gitignored — it holds your token, settings and launcher tiles.
  • Theme accent syncs to the phone — any #rrggbb works, not just the swatches.
  • Disable a feature and its commands are refused by the agent, not merely hidden.
  • Per-phone prefs (haptics, natural scroll, pointer speed) live in the phone's localStorage.

Full data/settings.json reference

data/settings.json — defaults shown
{
  // Server — these two need an agent restart to apply.
  "port": 8765,
  "bind": "0.0.0.0",          // e.g. a Tailscale IP to listen on that interface only

  // Shown as the app title on the phone (empty = "LapDeck").
  "deviceName": "",

  // Synced UI theme. Any #rrggbb accent works, not just the built-in swatches.
  "theme": { "accent": "#4c8dff" },

  // Feature switches. A disabled feature's commands are REFUSED by the agent.
  "features": {
    "screen": true,   // live screen view + tap-to-click (+ /stream.mjpeg)
    "input": true,    // touchpad + keyboard injection
    "files": true,    // fs.* protocol commands (browse/open)
    "media": true,    // volume + transport keys
    "power": true     // lock / sleep / shutdown / restart
  },

  // Per-action power permissions (only consulted when features.power is on).
  "power": {
    "allowSleep": true,
    "allowShutdown": true,
    "allowRestart": true,
    "graceSeconds": 5          // 0–60; countdown during which one tap aborts
  },

  // Screen-view quality presets. fps 1–15, width 480–2560, quality 20–90.
  "stream": {
    "presets": {
      "low":  { "fps": 2, "width": 960,  "quality": 45 },
      "med":  { "fps": 4, "width": 1280, "quality": 60 },
      "high": { "fps": 6, "width": 1600, "quality": 72 }
    }
  },

  // Custom keyboard buttons. keys = a chord or a comma-separated sequence. Max 30.
  "shortcuts": [
    { "id": "a1b2c3d4", "label": "Command Palette", "keys": "ctrl+shift+p" }
  ]
}

Launcher tiles — data/apps.json

Managed from the phone, but hand-editable. exec targets resolve like the Run dialog (App Paths), so bare names work without full paths.

data/apps.json
[
  { "kind": "exec",   "target": "chrome.exe", "args": [] },
  { "kind": "folder", "target": "C:\\dev\\project" },
  { "kind": "file",   "target": "C:\\notes.txt" },
  { "kind": "url",    "target": "https://youtube.com",
    "browser": "chrome", // optional: force a browser
    "icon": "youtube.svg" } // public/icons/ file or https URL
]

Re-pairing / rotating the token

Lock every paired phone out instantly and issue a fresh QR:

  • Delete data/secret.json.
  • Restart the agent (npm start).
  • Scan the new QR — old phones are locked out immediately.

Want alternative clients (a native app, a CLI, an automation script)? The full WebSocket protocol is simple JSON envelopes, documented in docs/PROTOCOL.md.

FAQ

Questions, answered.

Does it run on Windows and Linux?
Both. The same app auto-detects the OS at launch and picks the right backend — src/win/ on Windows, src/linux/ on Linux — behind a shared facade. On Linux both X11 and Wayland work: Wayland uses ydotool for input and grim for screen capture (needs the matching tools installed). macOS is a welcome contribution.
My phone can't connect.
The number-one cause is the firewall. On Windows, allow Node.js through Windows Firewall on private networks (the prompt appears on first run). On Linux, allow the port: sudo ufw allow 8765/tcp. Also confirm the phone is on the same Wi-Fi as the laptop.
Sleep hibernates instead of sleeping.
A Windows quirk: if hibernation is enabled, SetSuspendState hibernates. Run powercfg /h off if you prefer S3 sleep. On Linux, sleep uses systemctl suspend.
Does it work without internet?
Yes — everything is LAN-local. The internet is only involved if you choose to add Tailscale for remote access.
Can I control it from outside my home Wi-Fi?
Yes. Install free Tailscale on both devices — no port forwarding, no config. The agent detects it and shows a remote QR / URL automatically.
Multiple monitors?
Screen view currently shows the primary display only. Multi-monitor selection is on the wishlist — contributions welcome.

One npm start away.

Free, open-source, MIT-licensed. Turn the phone in your hand into the remote your laptop always deserved.