"Mirror your iPhone screen to Linux — real-time, no cloud, no accounts."
Screenshots · Install · Features · How it works · Development
iPhone Mirror captures your iPhone's screen over AirPlay and renders it inside a native GTK4 window on Linux, using hardware-accelerated GStreamer/VAAPI decoding. No accounts, no cloud, no telemetry — just your screen, on your machine.
Device frame matches your iPhone

- 🔍 Auto-detect iPhone model — reads the device identifier from AirPlay and automatically selects the matching frame (11 Pro Max, 12 Pro Max, 13, 14 Pro Max, 15, 15 Pro Max, 16, 16 Pro, 17 Pro, 17 Pro Max).
- 🖼️ Realistic device frames — each frame is a high-res PNG with the exact cutout dimensions from device-frames-media, so the video sits perfectly inside the screen.
- ⚡ 60fps streaming — UxPlay
-fps 60by default; changeable with--fps. - 🎨 Native GTK4 — no Electron, no webview; a single Python process with GStreamer/VAAPI hardware decoding and OpenGL ES rendering.
- 📐 Fixed frame size — the phone stays 640–900px tall regardless of window size; no oversized frames when you maximize.
- 🔄 Live orientation — portrait and landscape automatically.
- 🛡️ Local only — the RTP stream stays on
127.0.0.1; nothing leaves your machine. - 👆 USB touch control — click and drag on the mirrored screen to tap and swipe on your iPhone (requires USB connection +
pymobiledevice3).
sudo apt install ./iphone-mirror_0.3.4_all.deb./install.shInstalls to ~/.local/bin/iphonemirror — no root required.
git clone https://github.com/MUHAMMEDHAFEEZ/iphone-mirror-linux.git
cd iphone-mirror-linux
sudo apt install python3-gi gir1.2-gtk-4.0 gstreamer1.0-tools \
gstreamer1.0-plugins-base gstreamer1.0-plugins-good \
gstreamer1.0-plugins-bad gstreamer1.0-libav gstreamer1.0-vaapi
./install.sh| Package | Purpose |
|---|---|
python3-gi + gir1.2-gtk-4.0 |
GTK4 Python bindings |
gstreamer1.0-vaapi |
Hardware video decoding |
gstreamer1.0-plugins-bad |
H.264 depayloader |
UxPlay 1.74+ (-vrtp support) |
AirPlay server |
pymobiledevice3 (optional) |
USB touch control |
# Basic — auto-detect device, 60fps
iphonemirror
# Force a specific frame
IM_FRAME="15 Pro Max" iphonemirror
# Change max FPS
iphonemirror --fps 120
# Debug mode (shows detection logs)
IM_DEBUG=1 iphonemirrorTo control your iPhone from the PC (tap, swipe), you need:
- USB connection — connect your iPhone via USB cable, unlock it, and tap Trust This Computer if prompted.
- usbmuxd — the USB multiplexor daemon:
sudo systemctl start usbmuxd
- pymobiledevice3 — Python library for iOS device communication (iOS 17+
uses a RemoteXPC tunnel over USB for input injection, not the classic
lockdown/HID service):
pip3 install --user pymobiledevice3
Touch injection needs its own tunnel in addition to the AirPlay mirroring
connection — the app can't create it itself since it requires sudo. Two
ways to start it:
One command, one sudo prompt:
./run-with-touch.shStarts the tunnel, wires it up, launches iPhone Mirror, and tears the tunnel down when you quit.
Keep the tunnel up across relaunches (useful while iterating):
./start-tunnel.sh # in its own terminal — leave it running
iphonemirror # in another terminal, as many times as you likeHow it works:
- Click on the mirrored screen → tap at that point on your iPhone
- Drag on the mirrored screen → swipe gesture on your iPhone
- The coordinate mapping is automatic — click where you want to tap
Note: Touch control requires the iPhone to be connected via USB AND AirPlay at the same time. USB carries the input-injection tunnel; AirPlay carries the mirrored video. Losing either one drops just that half.
┌──────────────┐ AirPlay ┌──────────────┐ RTP/UDP ┌──────────────────┐
│ iPhone │ ─────────────▶ │ UxPlay │ ─────────────▶ │ GStreamer pipe │
│ (any model) │ ◀───────────── │ (1.74, -vrtp)│ ◀───────────── │ VAAPI decode │
└──────────────┘ AirPlay2 └──────────────┘ 127.0.0.1 └────────┬─────────┘
│
▼
┌──────────────────┐
│ GTK4 GLArea │
│ device frame │
│ 60fps render │
└──────────────────┘
- UxPlay advertises an AirPlay service (
iPhone Mirroron_airplay._tcp) and waits for a connection. - When your iPhone connects, UxPlay prints the device model (e.g.
iPhone12,5) and starts an RTP/H.264 stream to127.0.0.1:5000. - GStreamer receives the RTP packets, depayloads H.264, decodes with VAAPI (hardware), and converts to RGBA.
- VideoArea (a GTK4
GLArea) uploads each frame as an OpenGL texture, draws it inside the matching device frame PNG with alpha blending.
connection request from Ali's iPhone (iPhone12,5) with deviceID = A4:83:E7:12:34:56
UxPlay prints this line at the default INFO level. Our reader thread parses it, maps iPhone12,5 → iPhone 11 Pro Max, and hot-swaps the frame texture.
The frame is capped to 640–900px height — it never grows beyond that, even in fullscreen. This keeps the phone looking like a real device, not a stretched poster.
git clone https://github.com/MUHAMMEDHAFEEZ/iphone-mirror-linux.git
cd iphone-mirror-linux
pip install pygobject # for development only
python3 -m iphone_mirror.main --port 5000iphone-mirror-linux/
├── iphone_mirror/
│ ├── __init__.py # version
│ ├── main.py # CLI entry point
│ ├── app.py # Gtk.Application + MirrorWindow
│ ├── video.py # VideoArea (GStreamer → GL texture)
│ ├── gl.py # OpenGL ES ctypes bindings + shaders
│ ├── uxplay.py # UxPlay process manager + device detection
│ └── device.py # iPhone model → frame mapping
├── assets/frames/ # device frame PNGs + template.json
├── packaging/ # .desktop, icon
├── install.sh # user install (~/.local)
├── make-release.sh # build .deb + tar.gz
└── dist/ # release artifacts
./make-release.sh # produces dist/iphone-mirror_*.deb + .tar.gz
./install.sh # install to ~/.localThis is a solo-maintained local tool, not an audited enterprise product. The RTP stream is unencrypted (that's the AirPlay protocol), but it stays on 127.0.0.1 — nothing leaves your machine. UxPlay advertises on the local network via mDNS, so any device on the same Wi-Fi can see the service name, but only an authorized Apple device can connect.

