Site: kamilbourouiba.github.io/MirrorUE
Native macOS mirror for a development iPhone you own, trust, and authorize.
Swift / AppKit / Metal UI. Video uses the system CoreMediaIO screen device — the same USB path as QuickTime and Finder presentation. Touch, hardware buttons, and keyboard go through an embedded CoreDevice tunnel on a usbmux Network peer so the cable stays free for capture.
USB cable → CoreMediaIO (iPhone screen DAL) → Metal (IOSurface, zero-copy)
Wi‑Fi / Net → CoreDevice tunnel → UniversalHID (touch · buttons · keyboard)
(+ engine HEVC fallback until capture is live)
Not a general consumer screen mirror. MirrorUE is for phones you pair for development (Developer Mode, Trust This Computer). DRM apps may blank while mirrored.
Need path automation? See MirrorUE Pro (record / replay / export flows).
| Area | What you get |
|---|---|
| Video | USB CoreMediaIO → Metal, IOSurface zero-copy, target 120 fps (ProMotion) |
| Touch | Click / drag / scroll on the mirrored image; landscape digitizer remap |
| Keyboard | Auto layout mapping (fr / us / physical) from Mac language + input source |
| Dock | Home, Lock, App Switcher, Control Center, Siri, mute, volume, music-safe, instant |
| Capture | Screenshot & HEVC screen recording with show-touches |
| API | Loopback HTTP + CLI for tap, type, home, open app, frame grab |
| Window | Aspect-locked phone frame, portrait ↔ landscape follow, native fullscreen |
| Need | Detail |
|---|---|
| Mac | macOS 14+, Xcode Command Line Tools (swift) |
| Phone | iOS 27+, USB-paired, unlocked when prompted |
| Dev | Developer Mode on; Wi‑Fi / Network usbmux pairing for the HID tunnel |
| Rebuild | python3 -m pip install -r tools/requirements.txt |
./tools/package_app.sh # builds binaries + dist/MirrorUE.app + .dmg
open dist/MirrorUE.appFirst launch shows a short setup checklist (USB, Trust, Developer Mode, Network pairing, camera permission). Then pick your iPhone. Settings live in the dock gear or ⌘,.
Unsigned local builds: right-click the app → Open the first time.
./tools/build_engine.sh # once (or after pulling)
./mirroring # device picker, or pass --udid./mirroring --udid YOUR_UDID
./mirroring --width 390 --height 844Binaries land in bin/MirrorUE (UI) and bin/MirrorUEEngine (frozen CoreDevice
worker). The packaged .app embeds both under Contents/MacOS/.
| Action | How |
|---|---|
| Tap | Click on the mirrored screen |
| Drag / scroll | Click-drag or mouse wheel over the mirror |
| Rotate | Rotate the physical iPhone — the window follows |
If landscape taps feel mirrored:
MIRRORUE_LANDSCAPE=left ./mirroringUSB HID usages are US key positions. MirrorUE defaults to auto layout
detection (Mac language → phone HID map).
./tools/kb_translate.py --test
MIRRORUE_KB_PHONE=us ./mirroringClick once inside the mirror so it is first responder.
| Control | Action |
|---|---|
| Home / Lock / App Switcher | Hardware buttons |
| Control Center / Siri | System shortcuts |
| Mute / Vol ± | Volume |
| Music safe | Pause video path; HID-only |
| Instant | Soft refresh; Shift-click = hard reset |
| Screenshot | PNG to Desktop (⌘⇧S) |
| Record | .mov to Desktop (⌘⇧R) |
| Paste clipboard | Cmd+V on device (⌘⇧V) |
| Performance | Live fps / latency (⌘P) |
| Settings | FPS, keyboard, landscape (⌘,) |
While connected, a loopback-only HTTP API listens on
http://127.0.0.1:8090 (MIRRORUE_API_PORT).
./tools/mirrorue status
./tools/mirrorue home
./tools/mirrorue open Snapchat
./tools/mirrorue type 'hello'
./tools/mirrorue tap 0.5 0.8
./tools/mirrorue do home wait:0.8 open:Snapchat
./tools/mirrorue frame /tmp/phone.jpg
./tools/mirrorue help-api| Namespace | Endpoints |
|---|---|
| control | POST /v1/control/{tap,swipe,type,home,open,do,…} |
| vision | GET /v1/vision/frame?maxW=720&format=jpg&encoding=b64 |
Full reference: docs/API.md
See SECURITY.md — do not expose this port publicly.
| Variable | Default | Effect |
|---|---|---|
MIRRORUE_CAPTURE_FPS |
120 |
CoreMediaIO target FPS |
MIRRORUE_KB_PHONE |
auto |
Phone HID layout strategy |
MIRRORUE_LANDSCAPE |
right |
Home indicator side for landscape taps |
MIRRORUE_API_PORT |
8090 |
Local HTTP API port |
Prefer Settings (dock gear / ⌘,) for FPS, keyboard, and landscape in the app.
Sources/
MirrorUEApp/ AppKit window, Metal view, dock, keyboard inject
MediaKit/ CoreMediaIO capture + MUVS readers
DeviceKit/ usbmux discovery, CoreDevice bridge
ControlKit/ HID client, keyboard translator
tools/
mirrorue_engine.py PyInstaller → bin/MirrorUEEngine
mirrorue CLI for local API
Why Network for control? CoreMediaIO screen capture reconfigures USB; HID uses the Network usbmux peer when available.
| Symptom | What to try |
|---|---|
| Picker empty | Cable, Trust, unlock, Refresh |
| Black / stuck connecting | Unlock phone; quit other mirrors; replug USB |
| Touch OK, video laggy | Status must show coremediaio |
| Taps miss in landscape | MIRRORUE_LANDSCAPE=left ./mirroring |
bin/MirrorUE* missing |
./tools/build_engine.sh then ./mirroring |
Issues and PRs welcome for bugs, docs, and keyboard layout maps. Do not commit
bin/MirrorUE*, pairing records, or secrets.
./tools/kb_translate.py --test
swift build -c release --product MirrorUE
./tools/build_engine.shMIT © 2026 Kamil Bourouiba