Skip to content

Releases: i-is-evil-duck/OpenDisplayWebBridge

v0.4.0 — the first public build: runs on macOS 13

Choose a tag to compare

@i-is-evil-duck i-is-evil-duck released this 01 Oct 06:39

OpenDisplay Web Bridge — v0.4.0

Turns a spare iPad into a second display for a Mac, over the network. A sender
connects as it would to any receiver; this forwards the picture to the browser
over a WebSocket binding of the OpenDisplay protocol (pv 3).

What it does

  • Passthrough — the sender's H.264 reaches the browser untouched, for
    anything with WebCodecs. Zero work, zero added latency.
  • WebRTC/VP8 fallback — for devices that cannot decode H.264 at all. The
    bridge decodes and re-encodes in software, then hands pixels to libwebrtc as
    VP8. Chosen over H.264 inside WebRTC because libwebrtc statically links
    libvpx at no extra size, VP8 is mandatory-to-implement in every browser, and
    H.264 inside WebRTC would need a hardware encoder an unsigned app cannot
    reach.
  • Input — touch (began/moved/ended/cancelled, normalised) and
    scroll, injected into the Mac as real events.
  • Remote cursor — drawn at the sender's position, in the sender's sprite,
    and kept visible in fullscreen by preferring page fullscreen over the native
    iOS player, which cannot draw an overlay.
  • Live diagnostics — the receiver reports what it can see, so a black
    screen is attributable to the sender, the bridge or the receiver rather than
    guesswork.

Requirements

  • macOS 13 or later
  • An OpenDisplay sender (the real OpenDisplay.app; it discovers this over
    Bonjour and dials in on port 9000)
  • Any modern browser as the receiver

Try it

OpenDisplayBridge-v0.4.0.zip is self-contained: OpenH264 and libwebrtc are
bundled and every load path is rewritten, so it needs no Homebrew, no
DYLD_LIBRARY_PATH, and nothing from the machine that built it.

unzip OpenDisplayBridge-v0.4.0.zip
open OpenDisplayBridge.app

The window shows a 6-digit code and a URL. Open the URL on the receiver and
enter the code; the session cookie survives reloads.

Gatekeeper will object once. The app is ad-hoc signed, so right-click ->
Open, or xattr -dr com.apple.quarantine OpenDisplayBridge.app. A Developer ID
signature and notarisation remove that step, and both require a paid Apple
Developer account — a free one cannot notarise at all, and its certificates
expire after 7 days.

Building from source

./tools/build-openh264.sh    # software H.264 decoder, ~1 min
swift run                    # or ./tools/make-app.sh for a bundle

The OpenH264 step is not optional for a portable build. Homebrew's bottle is
built against a recent macOS (minos 26.0), so linking it makes the app
macOS-26-only; building the same source with a 13.0 deployment target does not.
brew install openh264 also works, if you do not need the older target.

The icon is generated rather than committed, by tools/make-icon.swift — an
.icns is a container of pre-rendered PNGs, so there is nothing reviewable in
a diff, and the drawing is.

Verification

  • 141 unit tests pass, and a smoke test drives a running bridge over HTTP and
    WebSocket: static assets, the pairing gate, cookie issuance, WebSocket gating,
    and the hello -> welcome -> streamConfig exchange.
  • Every new regression test was checked to fail with its fix reverted and pass
    with it restored, rather than assumed to be one.
  • The bundle reports LSMinimumSystemVersion 13.0 and the bundled dylib reports
    minos 13.0.
  • No absolute build-machine path survives in otool -L or the rpath list. An
    unzipped copy, launched from /tmp, loads zero libraries from outside the
    bundle or the OS.
  • Corrupting one byte of the OpenH264 tarball makes the build script refuse
    rather than build something of unknown provenance.

Not covered by anything here: a live sender changing resolution while a receiver
disconnects, with the software decoder active. That sequence needs real hardware.

Licensing

MIT — see LICENSE. NOTICE.md records the third-party attributions
(OpenH264 BSD-2-Clause, WebRTC BSD-3-Clause) and sets out the OpenDisplay
position: this is an independent implementation of a documented wire protocol,
not a fork, and no upstream source is included.