— A developer’s quiet battle with time itself

Most people writing JavaScript today get to enjoy a pretty luxurious toolbox: modern browsers, evergreen engines, fast dev cycles, hot-reloading, TypeScript, React, and APIs that actually behave like their documentation promises.

While I also have enjoyed those luxuries over the past years, some of my daily reality for the past years has been… slightly different.

I’ve been working on TV applications that run on modern devices like the web browsers on your PC, for iOS, Android, Android TV’s just to name a few.

But as part of my job at 24i I have also been developing software that had to run on old set-top boxes – devices that were already outdated when fidget spinners were still a thing (can you even remember those?!)..
Their browsers are custom, slow, and often missing features you’d expect from even the earliest versions of Chrome. ES5 is “modern JS”, scoping quirks regularly reappear like ghosts, and debugging feels like archaeology.

Before diving in, I can already hear some of you ask:

What’s a set-top box?

It’s the little device your TV operator (used to) gives you — the box that sits between your TV and the broadcast stream.
It runs the TV interface: channel lists, EPG/guide, video playback, apps, on-demand menus, etc. And many operators still use very old hardware with outdated browsers and extremely limited processing power.
In a lot of western countries, you either just run an app from your TV Operator directly on your Smart TV, or they give you a pretty decent bit of hardware.

Motorola Vip1003 Stb (set Top Box Price Digitalbox Motorola VIP1003

In countries in Eastern Europe, Africa and some parts of South America, many TV Operators still ship out very old and weak hardware to their customers.

This post is about that very particular chapter of my work, getting modern looking applications to work on those old Set-top boxes.


How we did it

One of the biggest challenges wasn’t just dealing with old hardware — it was doing it while supporting many completely different devices with a single codebase. To handle that, we built everything as a hybrid Javascript application.

In practice, that meant the UI and business logic ran in a browser-like environment, while anything device-specific was exposed through a set of stable interfaces. Each device family had its own native layer — a “bridge” — responsible for talking to the underlying hardware:

  • video playback engines

  • remote-control input

  • DRM modules

  • network requests

  • storage layers

  • performance-sensitive operations

The app never touched those parts directly. Instead, it communicated with abstracted APIs that looked the same across all devices. Old Broadcom boxes, newer Linux units, Android TV devices, and even mobile clients all implemented the same set of interfaces, even if the underlying technology was completely different.

This approach gave us two major advantages:

  1. One codebase. Many devices.
    We could build features once and ship them everywhere.
    If a customer deployed new hardware, we just implemented a new bridge for that device — the app itself didn’t have to change.

  2. Predictable behavior despite messy hardware.
    Even if a specific box had strange quirks, we abstracted those in the bridge layer – so that the app layer itself remained as clean as possible.
    The app only saw clean, consistent behavior.

Of course, this meant the bridge layer had to be incredibly robust. On some devices it talked to custom C++ modules; on others, proprietary middleware; on others, Android or WebKit APIs. But the abstraction let us ship complex features — playback widgets, EPG interactions, theming systems, full UI skins — without constantly rewriting everything per device.

It’s the reason such a wide range of hardware, from decade-old set-top boxes to newer smart devices, could run the same app and feel (mostly) consistent.


Why it’s challenging

  • No modern JavaScript features
    let, const, promises, async/await… nope. Everything must be written in a strict ES5 world, where you learn to love (or hate) callbacks all over again – many things could not even be polyfilled reliably.

  • Vendor-specific quirks
    Every device family behaves differently. One box leaks memory if you append too many DOM nodes. Another has a mysterious 16ms input lag. A third crashes when you combine CSS transitions and timers. You learn to adapt — or go insane.

  • No real browser devtools
    Debugging usually means logging everything, reloading, and praying. Sometimes you debug through a serial cable. Sometimes through on-screen logs. Sometimes you’re just guessing – and you learn to develop a lot of your own custom dev-tools to help you work with different devices.

  • Performance budgets from the Stone Age
    You become very efficient. Not because you want to — but because a 10-line function can freeze the UI if you’re careless.


What I learned

Working within these constraints forces you to truly understand the fundamentals: how rendering works, how event loops work, how memory behaves, and how to squeeze performance out of a system that doesn’t really want to give you any.

It also teaches discipline: clean architecture, predictable state, defensive coding, and the ability to read and write code that will run on every box your customers haven’t replaced yet.

Working inside this system for so many years — with its layers, constraints, cross-device compatibility, and long-term evolution — is where I learned real software architecture. Not the theoretical kind, but the kind you develop from maintaining a large platform used by many customers, handling legacy devices alongside brand-new ones, and ensuring everything continues to work as features grow and requirements change.


The funny part

Next to this, building “normal” web apps feels like stepping into a spaceship. React 18 feels like cheating. Writing TypeScript feels like luxury. And using modern devtools… honestly, sometimes it makes me emotional.


Why I’m glad I did it

Working with legacy devices gave me a kind of engineering “range” I’d never have gotten otherwise. It’s not glamorous work, but it teaches resilience, creativity, and a weird kind of appreciation for modern tech.

And honestly? When you get something smooth, fast, and reliable running on a device that should have been recycled 10 years ago — it still feels like magic and can be surprisingly fun and interesting.