← built

projector-remote

A Go server that lives inside my projector and turns any phone into its remote.

kind
side project
year
2026

§01 context

We carried a projector to a friend's place for movie night and nobody had the remote. No IR codes existed for it anywhere, so we plugged in a laptop and drove the menus over ADB, one input keyevent at a time. We never watched the movie. I did get an idea I couldn't drop.

It's now a Go binary that boots with the projector, serves a PWA remote, injects input straight into the kernel, and plays movies, shows and anime in its own player app. I reverse engineered the netmirror app for the catalog and wrote adapters for anime providers. Tailscale means the remote works from any of my devices.

§02 shape

How it’s put together

phone / laptop (PWA)
        │  HTTP + WS over LAN / Tailscale
        ▼
Go server, running ON the projector
 ├─ serves the embedded React build (go:embed)
 ├─ /ws        JSON message protocol for the remote
 ├─ /api/catalog  NetMirror + anime catalog, normalized
 ├─ deviceio   local command execution
 └─ playback   sessions, resume, next episode
        │                     │  WS /player (localhost)
        ▼                     ▼
 kernel input (uinput)    player app (ExoPlayer)
 + input text / am          HLS / MP4 + subtitles

§03 hard parts

The hard parts

  1. 01

    Making it survive a reboot

    ADB over TCP turned itself off on every boot because of a vendor init script. Fixing it meant understanding the boot chain and dynamic partitions, then patching one file inside a mounted, read-only partition without bricking the thing.

  2. 02

    The backdoor I wasn't looking for

    Digging through the boot scripts turned up a live OEM backdoor. I shut it down before I trusted the device on my home network again. That part is in the write-up, minus anything that identifies the unit.

  3. 03

    Input that doesn't lag

    Shelling out to input keyevent per press is slow. The server writes to uinput and the pointer node directly, and only falls back to input and am for text entry and app launches.

  4. 04

    A catalog that wasn't meant to be used like this

    Movies and shows come from the NetMirror API, which I worked out by reverse engineering their app. Anime uses AniList for metadata and an ordered list of provider adapters, so when one source dies the next one takes over.

stack

  • Go
  • WebSockets
  • React PWA
  • Android
  • Media3 ExoPlayer
  • Tailscale

next →

WhatsApp service

A plug-and-play WhatsApp Cloud API service any multi-tenant CRM can sit in front of.