When pairing with someone on a local app, you usually end up screen-sharing and narrating what you see. It's clunky โ no real interaction, no shared context.
Tandem wraps any local web server with a collaborative layer.
With one command, you and your teammates can see each other's cursors, track mouse clicks, follow each other's scroll position, inspect shared console logs, and see which network requests are failing โ all in real time, directly inside the app.
No SDKs to install. No code to change. Works with anything that runs on a port.
- Live Cursors: Synchronized mouse positions with sequential, zero-collision color assignment.
- Shared Console: Unified stream of logs (log, warn, error) and network events from all participants.
- Scroll Sync: Viewport tracking via shared scrollbar indicators and you can follow someone's scroll via Cmd+Click.
- Input Locking: Prevention of race conditions through field-level locking while typing.
- Host Controls: Permission management for interactive vs. view-only participants.
Clone this repo and run the install script:
./install.sh(Requires the .NET 10 SDK, the script will try to install it via Homebrew if not found)
Start your local app (e.g., localhost:), then:
tandem <port>Distribute the Guest URL printed in your terminal. Use --public to tunnel via ngrok for global access.
- Follow:
Cmd + Clicka participant's cursor to snap your scroll to theirs. - Annotate:
Option + Shift + Clickan element to leave a persistent annotation bubble. - Highlight:
Option + Clickan element to trigger a visual ping for all users. - Chat & Monitor: Click the chat icon to open the chat window and monitor all consoles. If you're the host (the person who ran the
tandemcommand), you can also manage access.
- Backend: .NET + SignalR + YARP (Reverse Proxy), Redis, Prometheus.
- Frontend: Vanilla ES Modules injected into the DOM at runtime.
- Single Page Context: Collaboration works best on a single page. If different participants navigate to different routes, their cursors and scroll positions will likely to desync.
- Interactions: Complex interactions like drag-and-drop or canvas-based drawing are not currently synchronized.
- Form States: While input locking prevents race conditions, the actual content of unsaved forms is not currently synced between participants.

