Turn your spare Android tablet into a second monitor for your Mac — free, open source, no subscription. A Duet Display, Sidecar and Spacedesk alternative for anyone who already owns an Android tablet or phone and wants a real extended display, not just screen mirroring.
Unofficial Android client for OpenDisplay, filling the role of Sidecar/Duet Display: a real extended display (not just mirroring), low-latency H.264, over WiFi or USB, with touch and scroll input — speaking the same network protocol as the official app, with no changes required on the Mac side.
Website & download · How it works · Screenshots · Features · Compare · Install · Build · Usage · License
The original OpenDisplay project only ships an iOS/iPadOS client. This repository is the Android
version: the wire protocol, framing and Annex-B handling are ported from the upstream Swift
client, while the Android UI and MediaCodec pipeline are written fresh in Kotlin.
MAC (sender, original project, unmodified) ANDROID (this project)
CGVirtualDisplay
→ ScreenCaptureKit → VideoToolbox H.264
→ TCP [4-byte length BE][Annex-B frame] ══════→ listens on port 9000
→ MediaCodec (Surface) → SurfaceView
← control JSON (hello, touch, scroll, ping) ════
The Android device listens and the Mac connects — that's what lets it work over WiFi with zero
manual setup, via mDNS/Bonjour discovery (_opensidecar._tcp).
No staged mockups — real hardware, real WiFi connection. More shots (settings, mid-session mirroring, the Mac's Displays panel) on the website.
- H.264 video reception via
MediaCodec(hardware decoding), straight to aSurface. - Handshake, ping/pong and mDNS discovery compatible with the original Mac sender's protocol.
- One-finger touch becomes a mouse click/drag; a two-finger gesture becomes scroll.
- Pinch to zoom into the video, client-side — works in both Mirror and Extend mode, no wire protocol change needed, touch still lands on the right spot on the Mac while zoomed.
- Overlaid remote cursor (dot or decoded sprite, mirroring the Mac's cursor).
- Foreground service: the connection survives the app going to background and resumes correctly after unlocking the screen.
- Auto-enters Picture-in-Picture when you leave the app while connected — same as Netflix/Prime Video — so the video keeps playing in a floating window while you use something else.
- Editable mDNS name; the settings screen also shows this device's own address for troubleshooting.
- Optional performance HUD (fps, end-to-end latency, RTT).
- Adaptive UI (Jetpack Compose) for phone and tablet.
For using an Android tablet as an external monitor, against the best-known alternatives:
| OpenDisplay Android | Duet Display | Spacedesk | |
|---|---|---|---|
| Price | Free | Paid | Free (core) |
| Open source | GPL-3.0 | No | No |
| Account required | No | Yes | No |
| Same protocol as OpenDisplay Mac | Yes | No | No |
| Central server | None | Yes | No |
- Android 8.0 (API 26) or higher. v0.0.7 and earlier crash on launch on Android 12–13
(API 30-33) —
WindowMetrics.getDensity()was gated behind the wrong API level; fixed in v0.0.8, use that or newer (reported by @gaeaearth, fixed by @edoardomich in #5). - Same WiFi network as the Mac (or a USB connection via
adb forward— see below). - The original OpenDisplay Mac app, unmodified.
- Tested on real hardware (Samsung Galaxy Tab S9 FE+ and Galaxy Note10+/Tab S7+, Android 12 through 16) — not yet across a wide range of devices.
Download the latest APK from GitHub Releases
and sideload it (adb install, or copy it to the device and enable "unknown sources").
The Google Play version is in closed testing — want in as a tester? Email josepacelli@gmail.com.
./gradlew assembleDebug # build
./gradlew installDebug # install on a connected device/emulator
adb logcat -s OpenDisplay:* # app logsRequires the Android SDK installed, with ANDROID_HOME/local.properties pointing to it.
To produce a signed release APK, set up a release keystore and a keystore.properties file at
the repo root (both kept out of version control) — see app/build.gradle.kts for the expected
format, then run ./gradlew assembleRelease.
- Open the app on Android — it starts advertising itself via mDNS and listening on port 9000.
- On the Mac, open the original OpenDisplay app in
extendmode (WiFi) and pick the Android device from the list. - If the Mac doesn't see the device, check that both are on the same WiFi network and that it isn't blocking multicast/mDNS traffic — the Mac app has no manual IP-entry field, so WiFi discovery is the only supported path (USB is the fallback, see below).
USB connection details (via adb forward, without requiring usbmuxd) are covered in
peetzweg/opendisplay's Mac/OpenSidecarMacApp.swift —
the Mac app dials plain TCP to a configured host/port override when set, which adb forward
can tunnel over USB to this app's existing listener on port 9000.
This is an independent client, maintained separately, that implements the same network protocol
as OpenDisplay (peetzweg/opendisplay) to interoperate
with the original Mac app without requiring any change to it. It is not affiliated with the
original project's author.
- @gaeaearth has been this project's most active early tester — reported the Android 12/13 launch crash (#4), the screen-timeout/ reconnect issues (#10, also the suggestion behind the idle-screen fix in #28), and the misleading manual-connection hint (#18). Thank you for the sustained, careful bug reports.
- @edoardomich fixed the Android 12/13 launch crash (#5).
- @jpbhdrey built the USB accessory (AOA) transport
(#33) — plug the cable, tap the
system's prompt, no developer options or
adbdance required. - @dyss1992 reported the Mirror mode aspect ratio bug on some Qualcomm decoders, with a controlled before/after comparison that made the root cause obvious (#44), and suggested the immersive fullscreen mode (#45).
One-person side project, built evenings and weekends, not a company. If it saved you from buying a monitor, a tip on Ko-fi helps keep it maintained and free for everyone.
GPL-3.0, the same license as the original project. See LICENSE and
NOTICE for copyright notices.

