Skip to content

How we built a reliable player for Android and browser

How Lucimark builds a player for Android and browser, with consistent playback, reversible updates, and content available offline.

Photo of Paulo R.

by7 min read

This article was translated with AI assistance and reviewed by a person.

Illustration of a store with a horizontal screen on the wall and a vertical totem displaying orange and cream compositions.
One shared base for different screen formats. Conceptual illustration.

In a digital signage network, an outage can mean someone has to go to the store to restart a device. An unstable connection can delay the schedule. An update can fix playback and, at the same time, introduce an incompatibility with the installed app.

This is the scenario we built the Lucimark Player for. The goal is to keep the schedule on screen, reduce visible interruptions, and recover automatically whenever the device's conditions allow it.

The Player is built on a web application, shared between the browser version and the Android app. That choice lets us reuse the playback logic. It also demands care: storage, updates, and hardware access work differently in each environment.

To guide these decisions, we work with a playback contract: run the schedule it receives, prepare the next piece of content, resume after interruptions, and provide signals that help explain a failure. This contract guides expected behavior on both platforms, within the limits of each.

A shared core, different capabilities

Maintaining independent versions of the player would mean evolving two implementations of the playback sequence, the cache, and failure recovery. With a shared core, these rules can be developed and verified together.

An integration layer adapts storage, device identification, and updates to each environment. On Android, it also provides access to device features that help control how the equipment runs.

Sharing the same base does not make the capabilities equivalent. The Android app integrates with system features to keep the screen active. In the browser, the Player also tries to keep the display on when it is fullscreen or installed as a web app, if the browser supports that; outside those conditions, the page needs to stay visible.

That distinction has to be part of the design: an unavailable capability needs a predictable behavior. For the network operator, what matters is knowing which conditions are needed to keep that screen running.

The playback sequence and what appears on screen

During playback, the player needs to decide which content is active and prepare the next one. At Lucimark, this sequence is organized by logic shared between the two platforms.

The player keeps the current content and prepares the next one. At the switch, it updates the content and the visibility indicator together. That coordination keeps the screen from showing something other than what is scheduled for that moment.

We also need to handle time carefully. The player tracks playback duration independently of adjustments to the device clock. That way, a clock correction does not disrupt the sequence in progress.

When the page becomes hidden, the player adapts how it tracks the sequence. When it becomes visible again, it re-syncs the playback state. This mechanism helps recover state after background execution limits. For continuous operation, the device needs to keep the player active and visible.

There is also a difference between selecting the next content and having that content ready to appear. A video may need time to show its first frame. That is why the presentation layer can temporarily hold the previous content while the next one gets ready, with wait limits. These measures reduce visible interruptions during transitions and need to be verified under real device conditions.

An update also needs a way back

On Android, we split updates into two paths:

PathWhat changesWhen it is needed
Web application updatePlayback logic and the interfaceFixes and improvements compatible with the installed app
Android app updateThe app installed on the deviceChanges to system integration and device features

This split lets us update the web application remotely without replacing the entire Android app for every change. We still need to preserve compatibility: a new version cannot require a feature the installed app does not yet offer. Rolling back to a previous version likewise requires considering both parts.

While developing remote updates, we hit a problem rolling back to the version bundled with the app. The player kept an old update flag even after the server instructed it to roll back.

The root cause was an important distinction: not receiving a new instruction is different from receiving an instruction to revert to the original version. We fixed that interpretation, along with the app reload, which could also reuse a stale copy of the page.

That case reinforced a development principle: verifying an update includes testing the way back and confirming which version the device is actually running.

Offline starts before the connection drops

The player can only keep going with the resources it already has. Resuming playback without a network connection depends on having the schedule and the necessary media stored on the device.

Offline playback starts with the schedule and media already stored on the device. Conceptual illustration.

We store registration data, the schedule, and media on the device. On startup, this local state lets us recover whatever was already available. Cached files considered healthy can be reused without blocking startup on a validation check for each media item. When revalidation fails, the local file can still serve playback.

The cleanup policy also has to respect the schedule: removing media based only on its age could delete a file the screen still needs to show. That is why references from the current schedule protect files from that removal.

On Android, compatible videos can use the device's file storage directly, depending on the features of the installed version. If a file goes missing, the player needs to recognize that gap and fetch it again once there is a connection.

We limit the number of simultaneous downloads and space out retries when failures happen. This helps control load when multiple devices come back online at the same time, such as after a power outage.

How far offline support reaches also depends on the content. Videos and images already stored behave differently from content that queries external data, live streams, and news feeds updated over the internet. The schedule needs to account for these dependencies so the network operator knows what will keep working during a network outage.

Understanding the outage is part of recovery

When a screen shows up as offline, the cause may still be unclear. An unavailable network, an app shutting down, and a power loss each call for a different operational response.

In the browser, a local log helps identify an unexpected shutdown. On Android, integration with the device adds signals for startup, low battery, and power disconnection.

This data helps identify the likely cause of the outage. An abrupt cutoff can prevent the event from being logged, and the available signals vary between devices. Some of the information only arrives once the player reconnects. That is why the diagnosis guides the investigation and helps operations decide the next step.

Reliability as a development standard

The shared core lets us verify playback rules in the browser and reuse them on Android. Native integrations, in turn, require testing on the device itself: the browser does not reproduce every startup, storage, and video condition of a real installation.

When evaluating a change to the player, we come back to practical questions:

  • Does the sequence stay correct through a content switch?
  • Can the already-stored schedule be recovered after a restart with no network?
  • Is the update compatible with the installed app, and does it have a rollback path?
  • Do the available signals help investigate an outage, and are their limits clear?

This is how we translate Screens that never stop into engineering work: build in continuity, reduce the need for intervention, and make failures easier to understand when they happen.

For anyone running a digital signage network, it is worth starting with a concrete scenario: if the power drops and the device restarts before the internet comes back, what will it be able to show — and how will you know what happened?

Anyone setting up or evaluating hardware can check that standard against the player requirements and the guide on how to get your first screen live.

Start with one screen, free

Start on the free plan with 1 screen. Upgrade when your network grows.

Photo of Paulo R.

Paulo R.

Founder and CTO

Founder of Lucimark. Builds the platform end to end — from the Android player on the screen to the Workers serving the API.

View Paulo R.'s full profile

Get the next posts by email

No spam — just a note when we publish something new about DOOH, product or engineering.

By subscribing, you agree to receive emails from Lucimark. You can unsubscribe anytime.

We use analytics cookies to understand which articles are read and how to improve the blog. You can accept, reject non-essential cookies, or choose your preferences.