---
title: "The 77-Inch Black Screen and a Variable Called status"
date: "2026-08-11"
publish_date: "2026-09-14"
excerpt: "A trivial webOS app to put my 3D printer's camera on the living room TV. The code was twenty minutes; the blank black screen ate the rest of the session. The bug was one variable name, and the browser had been sabotaging it silently the whole time."
tags: ["webos", "3d-printing", "debugging", "octoprint", "javascript"]
author: "Martin Harris"
canonical: "https://blog.nakomis.com/2026-08-11-undone-by-window-status"
approved: true
---

# The 77-Inch Black Screen and a Variable Called status

![An enormous television dominating a dark living room, showing nothing but black, while a tiny 3D printer on a side table glows warmly and works away unseen](images/undone-by-window-status-1.png)

My CR6-SE has a webcam pointed at its print bed, served by OctoPrint. My living room has a 77-inch LG OLED. These two facts sat next to each other for months before the obvious thought arrived: fancy writing an app for it?

A webOS app, it turns out, is just an HTML/CSS/JS web app packaged into an `.ipk`. And the app itself is almost trivial — mjpg-streamer serves MJPEG (`multipart/x-mixed-replace`), which drops straight into an `<img>` tag. Full-screen the image, add a reconnect handler, done. Twenty minutes of code.

The rest of the session went on a black screen.

## The network was the hard part, not the code

First obstacle, and a legitimate one: the webcam feed on my server is only exposed through nginx vhosts gated behind **mutual TLS** — and a sideloaded webOS app can't present a client certificate. There's no certificate store you can usefully get at; the TV is just not going to take part in mTLS.

The fix was to expose mjpg-streamer LAN-only on a fresh host port. Host 8090 rather than 8080, because 8080 was already taken by another container — a nice look-before-you-leap catch. The router only forwards 80 and 443, so a cleartext camera stream on 8090 never leaves the LAN. That went through home-infra as a proper PR and an Ansible deploy, which felt like a lot of ceremony for a printer cam, and was still the right call.

Stream URL: `http://172.29.0.32:8090/?action=stream`. 1280×720, 15 fps. `curl` confirmed frames coming out. The TV showed black.

## The red-herring parade

Three perfectly plausible theories, all wrong:

1. **mDNS.** The app was using `leia.local`, and webOS's resolver support for mDNS is dubious. A real gotcha in general — I switched to the raw IP anyway — but not the bug.
2. **A stale instance.** `ares-launch` will happily re-focus a suspended copy of your app rather than cold-loading the build you just installed. Also a real gotcha — you can iterate for some time on code the TV isn't running. Also not the bug.
3. **The cache-buster.** I'd appended a query param to defeat caching, and mjpg-streamer is picky about its query string. Ruled out with `curl`. Not the bug.

Each theory was reasonable. Each fix produced the same black screen. This is the debugging experience that erodes the soul: every change is an improvement, and nothing improves.

## The actual bug

The app had a status overlay — "connecting…", "reconnecting…" — and the code grabbed it like this:

```javascript
var status = document.getElementById('status');
```

In global scope, `status` is not a fresh variable. It *is* `window.status` — the legacy browser-statusbar property — and `window.status` coerces **any assignment to a string**. So that line silently stored `"[object HTMLDivElement]"`. A string. `.classList` on it was `undefined`, `showStatus()` threw a `TypeError` — and because `showStatus()` runs *before* `feed.src = …` in `startStream()`, the image source was never set at all.

Every black screen, every theory, every rebuild traced back to that one line. Modern desktop browsers are increasingly forgiving about this; the older WebKit inside webOS enforces the coercion strictly. Renaming the variable to `statusEl` fixed the entire app.

![A single innocuous-looking domino standing at the head of a long toppled chain that winds across a workbench strewn with cables, screwdrivers and a television remote](images/undone-by-window-status-2.png)

## How it was finally caught

Not by squinting at the TV, which is the important part.

webOS supports remote DevTools: `ares-inspect` opens an inspector endpoint, and `curl http://localhost:<port>/json/list` gives you the page's raw debugger websocket. From there a tiny Python script can drive the Chrome DevTools Protocol directly — `Runtime.evaluate` to read live state, `Runtime.exceptionThrown` to catch errors.

The first query read the DOM state: `feedSrc: ""`, and the status element missing its `show` class — proof the script wasn't running to completion, not that the stream was broken. The second surfaced the exception verbatim: `TypeError … reading 'add' at showStatus`, with a line number. Decisive. No guessing, no reinstall-and-squint loop.

That's the transferable lesson. The install → launch → stare-at-the-telly cycle is slow and tells you almost nothing; one programmatic read of the actual page state replaced perhaps a dozen iterations of it. On a device where you can't easily attach a keyboard or a console, drive the debugger over the wire.

## The payoff

End state: jog the printhead in OctoPrint, watch it move on 77 inches of OLED from the sofa. Objectively ridiculous. Deeply satisfying.

Four gotchas went into the project README so the next throwaway app doesn't pay for them again: the `var status` collision, the `.local`/mDNS trap, the `ares-launch` stale-instance behaviour, and the remote-DevTools recipe. The "throwaway" also got a single CONFIG object up top, because the same HTML is destined for the Sony Android TV in the office next — a thin WebView wrapper pointing at the same stream, which the port-8090 exposure already serves.

The cheapest-looking bug in this story was the most expensive. `var status` looks like nothing. It compiles, it doesn't warn, and the browser has been quietly turning your element into the string `"[object HTMLDivElement]"` since before your TV was smart.

---

*Martin Harris connects cloud infrastructure to the real world. If you've ever deployed infrastructure via pull request so you could watch a nozzle wiggle from the sofa, you're in the right place.*


{{donate}}
