The device on your desk, in your hand.
Stream Android devices, emulators and iOS simulators from your Mac to any browser at 60 fps, with live touch.
Agents drive it through a CLI, MCP or HTTP. In the browser you see each tap coming, and your touch takes over.
brew install bmcreations/tap/loupe
brew services start loupe
open "http://localhost:18456/?token=$(cat ~/Library/Caches/loupe/token)"The picker lists every Android device on adb and every AVD you have installed, booted or not. Open one to stream it. iOS simulators need simcap, built with your own Xcode:
brew install --HEAD bmcreations/tap/simcap && brew services restart loupeWithout a certificate the service answers on loopback only. To reach it from your phone, give it TLS: docs/getting-started.md walks through it with Tailscale, then pairing a phone and connecting an agent.
Open a device in the browser while an agent drives it, and the web client shows who has control and where the agent is about to tap.
- The agent claims the device. A second agent gets
claimedByand picks another one. - In the browser, you see its cursor before it taps. The banner reads "claude is driving", and Take over gives you control. Between an agent's calls the cursor stays where it last acted for 20 seconds, dimmed.
- Touch the screen and you're driving. The agent's input is refused with
humanHeldand a retry time, and the banner reads "claude is waiting for you". Mark a device hands off to refuse agents entirely. - Box what's wrong and send it. Flag for agent sends your note, the screenshot and the UI tree. An interrupt stops the agent mid-task.
The cursor, the banner and Take over are only in the browser. An agent
working alone sees none of them: the CLI, MCP or HTTP just answers
claimedBy or humanHeld. Claims and the human hold are in
docs/agent-api.md, flags and interrupts in
docs/flags.md.
Every command is also an MCP tool. Point Claude Code or any MCP client at
loupe mcp and it gets the same commands, with the same claims:
claude mcp add loupe -- loupe mcptap takes a node instead of a point: loupe tap <target> --text "Sign in"
finds it in the accessibility tree and taps its center, and --id and
--desc match the other fields. Two matches are refused with the candidates
rather than guessed at. Coordinates still work, in device pixels, the ones
ui reports in each node's center. sequence runs several steps in one
call, and stops at the first that fails. Drive from the accessibility tree
rather than from the video. boot starts a shut-down AVD or simulator, with
--wait for how long to wait (0 returns at once), and boot-status reports
on an AVD boot still running.
docs/cli.md covers the commands, docs/mcp.md
the tools, and docs/agent-api.md every HTTP route.
Streams devices
- Android phones over adb. One plugged in later appears in the picker; one unplugged stays listed as missing, with the reason.
- Every installed AVD, booted or not. Opening one boots it from a warm snapshot. An idle emulator is paused after 5 minutes and shut down after 30. Save & Shut Down keeps what you installed for every later boot, Reset returns one to a clean state, and New device creates an AVD from the system images you have.
- iOS simulators through simcap. Physical iPhones and iPads are a preview: they stream over a cable, and input is still being worked on. See docs/ios.md.
- Devices on your other machines. Add another loupe server as a peer and its targets join your picker. See docs/remote-targets.md.
Feels like the device is in your hand
- Video goes over WebTransport (HTTP/3), with WebSocket as a fallback. Adaptive bitrate lowers the encoder's rate when the link can't carry it and raises it again when the link recovers.
- Multi-touch, swipes, hardware buttons, and your phone's own keyboard.
- The clipboard works both ways, and can be shared across every open target and the Mac's pasteboard.
- Add it to the Home Screen and it runs full screen as an app.
Lets agents work beside you
- The accessibility tree with a precomputed
centerper node, tap by text, resource id or description, swipe, text, keys, waiting for a node, app launch, deep links, screenshots, APK install, logs, crash reports, boot and shutdown. On a debuggable Compose app, each node names the composable that drew it, with its source line. camerapoints an AVD's, simulator's or iPhone's back camera at an image, so a scanner can be tested without holding a phone up to a screen.- Claims, the human hold and flags let you share a device with an agent. See Work on the same device as your agent.
- Recording writes the encoder's output to an MP4, with no re-encode.
Fits into your workflow
- A GitHub Action installs a pull request's APK on a target and comments on the PR with a link that opens it. See action/README.md.
- Extensions are separate programs that loupe proxies under
/api/ext, and they can draw overlays on a device. See docs/extensions.md. loupe pairgives a device its own session, which you can revoke without re-keying every other device.
Anyone who can reach the port can drive every device on it, so every request needs the shared token or a paired session, and plain HTTP is refused on anything but loopback. The token and every session have full access; there are no scopes yet. docs/security.md says what is protected and what isn't.
git clone https://github.com/bmcreations/loupe && cd loupe
make build
./loupe -addr 127.0.0.1:8080 -synthetic -no-devicesGo is all it takes. The scrcpy-server jar is embedded and the web client has no build step, so there is no scrcpy to install and no npm. The test pattern needs ffmpeg; it should hold about 60 fps in the stats overlay before you plug in a device. docs/running.md lists every flag.
| Doc | For |
|---|---|
| getting-started.md | From an install to a phone, a service and an agent |
| cli.md | loupe <command>, claims between commands |
| mcp.md | loupe mcp and pushing flags into Claude Code |
| agent-api.md | Every /api/* route, claims and the human hold |
| targets.md | AVDs, Android devices, the phone keyboard, the clipboard, device frames |
| ios.md | iOS simulators, and physical iPhones in preview |
| running.md | Every flag, the settings file, the stats overlay, tests |
| security.md | The token, TLS, pairing and sessions |
docs/README.md indexes the rest: architecture, the wire protocol, flags, recording, extensions and notes for working on the code.
loupe is Apache 2.0. It embeds and redistributes the scrcpy-server jar,
which is also Apache 2.0; third_party/scrcpy/LICENSE and
THIRD_PARTY_NOTICES.md carry that text and the upstream attribution.
Section 6 grants no trademark rights: nothing here may imply this is scrcpy
or that Genymobile endorses it.
simcap, the iOS helper, is also Apache 2.0 and lives in its own repo, bmcreations/simcap. It calls private frameworks from your own Xcode install; docs/ios.md says which, and what that means for an Xcode update.
iPhone, iPad, Mac, macOS and Xcode are trademarks of Apple Inc. loupe is not affiliated with Apple.