r2d2-tv: A Local Home Screen, Private Browser, and Voice Assistant for webOS TVs
What r2d2-tv is
r2d2-tv is an open-source project that reduces a webOS smart TV to a display plus a remote, then adds back only the smart features its owner chooses: a local home screen, a privacy-filtered remote browser, and a push-to-talk voice assistant. All browser rendering and speech model inference run on a separate aarch64 Linux machine on the same LAN, not on the TV and not in a vendor cloud.
The project was published by GitHub user anubhavmalhotra at https://github.com/anubhavmalhotra/r2d2-tv and submitted to Hacker News as “Show HN: Making a smart TV dumb and then smart” (item 50202865). The repository links a demo video at https://anubhavmalhotra.com/vibing/r2d2-webos/r2d2-demo.mp4 and a longer write-up titled “R2D2 for webOS.”
The architecture: one Linux host, one TV, three code areas
r2d2-tv is built on a strict split of responsibilities. The Linux compute host does the heavy work; the TV provides the display, the applications, and the remote control. The repository describes three cooperating parts.
Compute host (spark/)
The host is an aarch64 Linux machine. It renders a sandboxed Chromium session inside a private display and audio session and streams that session to the TV over the LAN. The same host runs the local speech recognition and intent models and a closed command registry, meaning the set of voice commands is fixed at build time rather than open-ended.
TV side (tv/, voice/tv/)
The TV runs a Home application and a browser viewer. The viewer sends remote input back to the host. A root capture owner on the TV takes the remote's voice key and sends bounded audio to the host over mutual TLS.
Content protection (protection/)
Content blocking is implemented as pinned filter rules loaded into each disposable browser profile, so each browsing session starts from a clean, filtered profile rather than accumulating state.
What stays local, and what still leaves the network
Model inference stays on the host. That is the core privacy claim of the project.
The repository is explicit that this is not a blanket offline mode: requested websites, searches, streaming apps, and the optional weather feature still contact their own providers. In other words, r2d2-tv removes the TV vendor from the data path — the vendor's home screen, recommendations, and always-on voice pipeline — but it does not anonymize the sites a user chooses to visit. Security boundaries and reporting are documented in the repository's SECURITY.md.
Requirements
Running r2d2-tv requires specific hardware and software on both sides.
- Build machine: Python 3.10 or newer and Node.js 20 or newer.
- Compute host: aarch64 Linux with a non-root service account (UID 1000); the Chromium snap with its sandbox enabled; Xvfb for a private X display; PipeWire, WirePlumber, and the GStreamer plugins for the WebRTC path; FFmpeg with hardware HEVC encoding for the MSE and AirPlay paths; a CUDA-capable GPU and Python 3.12 for the pinned local models, which carry roughly 10 GB of weights.
- TV: a webOS TV for which the owner has system (root) access and an authorized way to install packages. Native voice capture additionally needs a toolchain and headers matching the exact firmware build.
- Network: TV and host on the same LAN. The project advises measuring throughput and jitter, because a nominal link rate does not establish playback quality.
Build, test, and deploy
Build
Building does not install software or contact a device, and the output directory must be new. The documented flow copies an example configuration to a git-ignored file, edits it for the user's devices, then runs the builder to produce an unsigned Home package, rendered host services, TV service sources, and a file manifest.
Enabling the browser shortcut requires generating a private capability key (a 64-character hex token created with the Python secrets module under umask 077) and passing it to the build with the browser-key-file option. Brand logos are deliberately not shipped: the home screen shows text tiles unless a private JSON file mapping app IDs to allowed data-URI logos is supplied. The rendered output is meant to stay private because it contains deployment addresses and may contain the browser capability.
Test
Offline testing runs Python unit tests plus Node test files for protection proofs and home-screen core logic, then a privacy check script. UI tests use Playwright's Chromium for the home and browser surfaces. The full list of offline checks lives in the repository's CI workflow file. The project warns that offline checks do not establish physical video cadence, microphone quality, or compatibility with another firmware build.
Deploy the host
The host deployment model is manual. A complete rendered spark/ tree is staged as a versioned directory owned by the service account, and its preflight is run in check-only mode before the session manager is enabled. Preflight verifies the sandbox, display and audio, content protection, DNS, and cleanup. It does not install packages or verify TV playback, and there is no general host installer — dependency installation and service supervision are left to the operator as ordinary host administration.
The runtime keeps its capability key and optional voice configuration under the service account's home directory in .local/share/baaz-browser/. The project notes that baaz-browser is a legacy runtime directory name kept for compatibility, and that these files must be mode 0600 inside private directories. Resident voice additionally needs pinned model snapshots, TLS credentials, and a locally paired TV control.
Install on the TV
The recommended order is to install the Home package through the webOS package workflow and test it as an ordinary application first. Changing Home and idle routing is a separate, later step: an offline Home planner inside tv/integration/ produces candidate changes and rollback payloads from the firmware's captured state.
Voice capture follows a documented local-capture activation contract. The public builder cannot gain device access, patch system files, or activate services by itself. The project instructs users to keep original files and a recovery path outside any process that a UI restart would terminate, then to verify remote controls, existing apps, browser playback, consecutive voice presses, standby and resume, and power cycling.
The risks: root, firmware pinning, and warranty
r2d2-tv is not a safe, general-purpose modification, and the repository says so up front.
The TV components run as root and modify vendor system services on one specific firmware build. The stated consequences are that the modification is unsupported, may void the warranty, and can leave the TV unusable until a reboot or a factory reset.
Three specific mechanisms carry that risk: the keyfilter patch, the Home and idle routing changes, and native voice capture. Each replaces or hooks a vendor service, so a mistake can disable the remote, the Home button, or voice until the TV is restored. Firmware SHA-256 pins, code offsets, symbol names, and patch anchors all target a single reference build; the project tells users to re-derive them for their own firmware and never to force a mismatched pin.
Current status is narrow by design
The reference deployment covers one TV and one firmware build. On that configuration, Home, the browser, and remote voice commands work end to end. Every other combination of TV, firmware, remote, and host is described as unvalidated.
The repository ships source and offline tests only. It is explicitly not a universal root tool, firmware installer, or host installer. It is also an independent project, not affiliated with, endorsed by, or sponsored by LG Electronics, Lucasfilm, The Walt Disney Company, or any streaming service.
Licensing
Project code is MIT-licensed. The browser extension, the filter tooling and its data, and two runtime adapters are GPL-3.0-or-later. A third-party notices file lists those components and their upstream attributions.
Community reaction: alternatives and the HDMI baseline
The Hacker News discussion was small — the post scored 37 points with 7 comments — and the comments mostly framed r2d2-tv against simpler alternatives rather than critiquing its implementation.
One recurring position is that the ideal television does almost nothing. As one commenter put it:
All I really want my TV to do is turn on, quickly and reliably, and display whatever is plugged into HDMI1. The market is flooded with “Smart” (actually Surveillance) TVs so not buying one isn't really practical.
That comment captures the tension the project addresses: people who do not want a surveillance-capable TV still find it impractical to avoid buying one, so stripping the software after purchase becomes the pragmatic path.
A second commenter pointed to a related project for cleaning up TV software, at https://github.com/Gathin23/cleanyourtv/tree/main, indicating a small ecosystem of tools aimed at the same problem from a different angle.
A third commenter argued the simplest solution is to skip smart features entirely:
A dumb TV connected via HDMI to a Raspberry Pi running Kodi is smart enough for me.
The counterpoint implicit in these replies is that r2d2-tv is the high-risk, high-integration option in a space that also contains a low-risk HDMI-box approach and other firmware-cleaning tools. r2d2-tv is the choice for someone who wants the TV's own remote, home screen, and voice button to keep working while the vendor's software layer is replaced.
Source map quick reference
| Path | Purpose |
|---|---|
tv/home/ |
Home application templates |
tv/integration/ |
Offline Home and idle-return planner |
spark/ |
Browser, media transport, and session manager |
voice/ |
TV capture, local models, and validated command execution |
protection/ |
Content-blocking rules and reproducible extension builder |
tools/ |
Private build, package, and privacy tools |
The repository's guidance is to keep device configuration, keys, firmware images, and runtime data outside version control.