Unofficial port of CleanFrame to LG TVs running webOS 6 or newer. Not affiliated with the original project and not maintained by its author.
CleanFrame was written for the Samsung The Frame (Tizen): one photo, a large clock, your own apps.
The idea was a start screen without the clutter a manufacturer ships. This port keeps the original
layout, focus logic, settings, wallpapers and materials, and replaces only the device layer.
app/device.js talks to webOS itself instead of the Tizen API: apps are launched through
com.webos.applicationManager, and Glasshouse is
used only where it is convenient, never required.
The start screen: one photo, the clock, the apps you actually use. Rendered at 1920×1080, without the TV's own chrome around it.
The hub in the browser: every setting of the app, saved as you change it, plus a live picture of the TV and a remote for the cases where walking over to the TV is too far.
Add it to the Homebrew Channel: one address, then install and update from the channel. Or take the IPK from the release and hand it to Glasshouse. The settings page next to it is optional.
| Part | What it is | Where it runs |
|---|---|---|
| Launcher | The start screen as a webOS app (com.blackpearl.cleanframe), packaged as an IPK |
On the TV |
| CleanFrame Hub | Configuration server and web interface: edits settings, shows a live picture, sends remote key presses | Also on the TV, reachable from the LAN |
The hub owns the configuration. The launcher reads it at start and every two seconds afterwards, so a change made in the browser is on screen within seconds, without restarting or reinstalling anything.
The install has two parts because the two parts need different things.
The launcher is a normal webOS app. It needs root once, because installing any package on a stock TV
goes through the app installer and that is only reachable with a root shell. Afterwards the app runs
without root. The hub stays root-only: it lives in /var/lib/cleanframe, starts itself at boot and
reads the TV's internals for the live picture.
Root is therefore a prerequisite, not a feature of this project. Getting it belongs to the Homebrew Channel: their guide covers each generation and they keep up with firmware updates. The path used on the test TV here, SlopBro plus a firmware check, is documented in docs/ROOTING.md and is explicitly unsupported.
- List layout and bottom dock, photo, light or dark background, 15 wallpapers with rotation, and a picture of your own when you want one
- Typography, focus and material styles, clock in 24 h or 12 h, German and English interface
- Senior mode: larger labels (at least 125 %) for the person actually watching
- Own app catalogue: installed webOS apps, HDMI inputs, and system apps Glasshouse does not list
- Rename tiles, choose icons, reorder or remove them, with a context menu on the remote
- Web interface without a PIN: token optional, requests from the local machine always allowed
- Home Assistant values as a status strip on the start page (your labels, states translated, no restart needed); MQTT and update blocking through Glasshouse
- Weather next to the clock: a thin symbol and the temperature, position and size up to you, fetched by the hub from Open-Meteo so the television stays offline
- Screensaver, setup wizard, backup and restore of the configuration
Where the firmware number is on the TV, and the two ways to find it: docs/FIRMWARE.md.
Your own picture goes in the settings page, Appearance, Background: pick a file, it is cropped to
1920x1080 and stored next to the configuration in /var/lib/cleanframe/walls. The television
loads it from the hub, so it works as long as the hub is reachable; the packaged wallpapers work
without it.
tools/install-wizard.py asks, checks, installs — and stops when something is not
right instead of pretending it worked.
./tools/install-wizard.py # finds the TV on the network
./tools/install-wizard.py --tv 192.168.0.49 # or takes the address
./tools/install-wizard.py --dry-run # prints the plan, changes nothingIt finds the television, checks whether it answers and what it is, and checks root. Without root it offers to run a published exploit — not ours, and not shipped here: see Rooting. Then it installs or updates Glasshouse, offers to block LG's advertising endpoints, installs CleanFrame and the hub, and ends with one address to open in a browser:
http://<tv-ip>:8484/ the launcher's settings, everything else is set there
http://<tv-ip>:8080/ Glasshouse next to it
Every step says what it is about to change before it changes anything, everything it
ran lands in ~/.cleanframe-install.log, and --yes (for a machine nobody sits at)
does the safe steps only — no rooting, no blocking.
The wizard contains no exploit and does not patch one. It reads the webOS version and names the project that covers it:
- SlopBro — throwaway96, AGPL-3.0, webOS 4 to 11
- DejaVuln autoroot — throwaway96, AGPL-3.0, webOS 3.5+
- faultmanager autoroot — throwaway96, AGPL-3.0, webOS 4.0 to 10.0
- RootMyTV — MIT, webOS 3.5 to 6.3.0, not firmware after mid-2022
Then it shows the licence, the project page and the risks (a bricked TV, a lost
warranty, patched firmware on anything recent), and runs nothing until ERISK is typed.
The files come from the project's own release, not from this repository.
Fetched from its releases, verified against the sha256 in its manifest, then handed to the TV. Prereleases are never offered. If the package is already installed, the wizard only updates it when you say so, and keeps its configuration — including the network access it asks for on first start, which has to be granted on the television itself once.
- an LG TV running webOS, rooted. In practice that means the Homebrew Channel; how to root a TV is its guide's job, not this README's
- optionally Glasshouse, its dashboard on port 8080. It is the easiest way to put the IPK on the TV, and if it is there the launcher borrows the app list and app icons from it. Without Glasshouse the launcher asks webOS itself, so the dock still fills, only the app icons stay empty
- a way to get an IPK onto the TV: the Homebrew Channel, Glasshouse (Apps, install from a file) or webOS Dev Manager
| TVs | What to expect | |
|---|---|---|
| Tested | LG 43NANO779PA (NanoCell 2021), webOS 6.5.0-2401, Chromium 79, Glasshouse 0.118.0 | Everything in this README |
| Very likely | other webOS 6 sets (2021, "webOS 6.0") | Same browser engine, not tried |
| Untested | webOS 7, 9, 10, 11 ("webOS 23" to "webOS 26"), OLED C and G series | No idea. That is what testers are for |
| Not supported | a TV without root or without the Homebrew Channel | The launcher is an app, but the hub needs a root shell |
Two routes that work, and the scripts, which are experimental and not the place to start.
| Route | How | Needs | Updates |
|---|---|---|---|
| Homebrew Channel | add one repository address, install from the channel | a rooted TV with the channel | come by themselves |
| By hand | Glasshouse → Apps → install from a file | the IPK | install the new IPK again |
| Scripts | tools/install-launcher.sh, tools/install-hub.sh |
curl, python3, SSH |
experimental |
The launcher is one IPK, and since 0.5.3 the hub travels inside it. It starts with the app and stops with it, which needs no shell at all. Running the hub all the time is the optional second step.
This is how the other launchers in the scene are distributed, and from now on the normal way here too. The channel reads a repository file, and every later version arrives through it without you doing anything.
- Homebrew Channel on the TV, Settings, Add repository
- paste this address:
https://github.com/TheLord83/CleanFrame-webos/releases/latest/download/repo.json
- CleanFrame appears in the channel's app list, install it there
- start CleanFrame once from the app list; it writes its configuration on first start
The address points at the newest release, so it never has to change. If you would rather pin what
you install, the same file is in the repository as repo/apps.json
(raw.githubusercontent.com/TheLord83/CleanFrame-webos/main/repo/apps.json). tools/make-apps-json.py
writes both from the built IPK, with hash, size, manifest and download URL.
Useful when the channel is not installed yet, or when you want to see exactly which file goes onto the TV.
- get the IPK from the latest release,
or build one:
python3 tools/build-ipk.py app dist/cleanframe_all.ipk --hub web - Glasshouse → Apps → install from a file → pick the IPK → confirm the version prompt
- start CleanFrame once from the app list
The hub is the part with the browser interface: Home Assistant values, the entity selection, MQTT, the remote, backup and restore. It is a node process on the TV, and the IPK carries it, so after an install from the channel you open
http://<tv-ip>:8484/
and the page is there. app/hubboot.js starts the server through the Homebrew Channel's service when
CleanFrame starts, waits until it answers, and stops it when the app goes away. Nothing permanent
happens on the TV, no shell, no root, no autostart entry.
The trade is time. A bundled hub lives as long as the launcher's web page does. Put another app on screen and it keeps answering, because webOS keeps the page in the background; after a reboot nothing runs until CleanFrame starts once, and that is the case this exists for. Measured on the test TV: the page answered for four seconds after the start, and stayed up while another app was in front.
If you want the page to answer while the TV is showing something else, install the hub for good. That needs a root shell and installs the autostart hook:
./tools/install-hub.sh --tv <tv-ip>or by hand:
scp -r web root@<tv-ip>:/var/lib/cleanframe
ssh root@<tv-ip> 'chmod +x /var/lib/cleanframe/cleanframectl && /var/lib/cleanframe/cleanframectl restart'Both modes use the same configuration file, and a hub installed for good wins: the bundled one sees port 8484 taken and stays quiet, so there is never a second server. Details, including the autostart hook: docs/INSTALL.md.
There are three: install-launcher.sh (IPK through Glasshouse, no SSH), install-hub.sh (the hub
over SSH) and install.sh (both, and it never roots anything). They are written, they run, and they
have been used on exactly one TV: the 43NANO779PA in this README. No second model, no second webOS
version, no Windows machine and no macOS.
Use one of the routes above unless you want to debug them. They find the TV on the network, build the IPK, upload it and verify the result, which is convenient, but each of those steps is a guess about your network.
./tools/install-launcher.sh --tv <tv-ip> # the app, no SSH
./tools/install-hub.sh --tv <tv-ip> # the settings page, SSH as root
./tools/install.sh --tv <tv-ip> # both
./tools/install.sh --info # model, webOS, node, root stateThey stop instead of guessing: no Glasshouse means no install, a non-root session means no hub.
--dry-run prints the plan, --lang de answers in German, --help lists the rest.
Uninstall CleanFrame from the channel or from Glasshouse, and the app is gone. If the bundled hub
ran, one thing stays behind: /var/lib/cleanframe/config.json, which holds the Home Assistant token
and the MQTT password. That directory belongs to a permanent install as well, so nothing deletes it
automatically. Remove it yourself if you want it gone:
ssh root@<tv-ip> 'rm -rf /var/lib/cleanframe'A permanent hub is removed properly by ./tools/uninstall-web.sh <tv-ip>: it stops the service,
drops the autostart hook and deletes the directory in one go.
python3 tools/build-ipk.py app ipk/cleanframe_all.ipk --hub web # IPK, hub included
tools/check-all.sh # all checks, no TV required
tools/make-port-patch.sh # refresh docs/PORT-CHANGES.patchtools/check-all.sh starts its own configuration server and a headless Chrome, then runs syntax,
node 8 compatibility, icon, API, remote-key, live-apply and link checks in one go. Details in
docs/TESTS.md. The same script runs in CI on every push.
- docs/INSTALL.md: from a stock TV to a running start screen
- docs/ROOTING.md: what root is for, and where it comes from
- docs/TROUBLESHOOTING.md: symptom, cause, command
- docs/WEB.md: hub API endpoints, configuration schema, operating the service
- docs/TESTS.md: what is tested and how
- docs/ICONS.md: icon library and its sources
- docs/asset-sources.md: every photograph, its photographer and its licence
- docs/PORT-CHANGES.patch: full difference to upstream
app/device.js is new. app/main.js has a contained delta (webOS app IDs, LG labels, remote
handling, test hooks), app/style.css has Chromium 79 compatibility fixes, app/i18n.js and
app/appinfo.json are new, and app/config.xml (Tizen packaging) is gone. Everything under
app/assets/ keeps the original files untouched: everything that was there is byte-identical, apart
from SOURCES.json and PHOTO-CREDIT.md, which were extended, and everything else is new.
Rooting and sideloading third-party packages can affect warranty and DRM features; install at your
own risk. The app ID is com.blackpearl.cleanframe, and it installs through Glasshouse's sideload
path like any other homebrew app. What is tested and what is not: see Which TVs.
Open an issue for anything that is not a security problem. A report is worth ten times more with the model, the webOS version and a photo of the screen. Security problems go to the Security tab of the repository, Report a vulnerability, not into a public issue.
- Port code (webOS bridge, packaging, build scripts): MIT, see LICENSE. Both copyright lines are in there, the original one and the port's
- Original CleanFrame: MIT, Nathan Clark
- Interface and assets come from CleanFrame: MIT for code, separate terms for fonts (OFL), icons (CC0/MIT) and photos (Unsplash Licence). See THIRD_PARTY_NOTICES.md and docs/asset-sources.md
- Brands such as Netflix, YouTube or Disney+ belong to their owners. This project claims no affiliation and no endorsement
Deutsche Fassung: README.de.md

