A custom app is a complete, self-contained website that you package as a .zip file
and play on your screens like any other media. Everything is stored on the device, so the
app keeps playing when the internet drops. Custom apps were previously called Offline HTML.
EasySignage hands every custom app a set of player attributes: the identity and context of the screen it is running on, including the player ID, the screen name, the screen or display-unit ID, the resolution, the location, and your custom tags. Your app reads those values and changes what it shows, so one package behaves differently on every screen. The attributes also use Broadsign-compatible key names, so a creative migrated from Broadsign can keep its field names.
With custom apps you can:
- Show the right content per screen, per store, zone, floor, region, or language
- Personalise API and data requests using the screen’s ID or tags
- Keep playing reliably offline, with no server round-trip on every frame
- Reuse one package across hundreds of screens instead of building one for each
New to this? You do not need to write code to try it. Download the ready-made sample package and upload it, and the page displays every value your screen delivered. Developers can then copy one file into their own project to read the same values.
Download the sample
To see how custom apps work, run the sample package. It shows every attribute the device delivered, live on the screen.
Ready-made custom app sample
Download sample.zip
The .zip contains three files:
| File | What it is |
|---|---|
index.html | The page itself, and always the entry point. Markup only. |
easysignage.js | The small, reusable reader you copy into your own project to get the player attributes. |
demo.js | Throwaway code that draws the values on screen for the sample. Ignore it in real projects. |
Required: the entry point of every custom app must be named
index.htmland sit at the top level of the.zip. Keep all assets, images, CSS, JS, and fonts, inside the same package and reference them with relative paths so the app works with no internet connection.
Quick start without coding
- Go to Media and upload the
.zippackage (see Upload Media ). - Add the media item to a playlist.
- Assign the playlist to a screen.
The sample page appears and lists every attribute the screen delivered, including a banner that confirms which delivery channel is active. That confirms the integration works end to end.
How the data reaches your app
EasySignage delivers the same player attributes through two channels, so your app keeps working no matter how the creative is built or secured:
You don't choose a channel, easysignage.js reads whichever is available.
1. A global JavaScript object
EasySignage injects a small script into your index.html that defines a global object
before any of your own scripts run:
window.easysignage // the player object, e.g. { player_id: "...", tags: { zone: "north" }, ... }
2. A URL query string
The same values are also appended to the page URL on every render:
index.html?player_id=PL-abc123&name=Main%20Lobby&display_unit_id=SC-9&tags.zone=north&...
The query string is always present. If a strict security policy on your creative blocks
the injected script and the global is missing, your app still reads the values from the URL.
Keys that start with tags. (for example tags.zone) rebuild into a tags object.
You normally don’t choose between them. The
easysignage.jsreader uses the global when it is available and falls back to the URL automatically. You call one function.
For developers: read the attributes
Copy easysignage.js from the sample into your project and load it with a plain script
tag before your own code:
<!-- index.html -->
<script src="easysignage.js"></script>
<script>
var panel = EasySignage.read(); // the player object (global or URL — handled for you)
var playerId = panel.player_id || "";
var screen = panel.name || "";
var zone = (panel.tags && panel.tags.zone) || "";
// ...use these values to drive your creative.
</script>
That is the whole integration. EasySignage.read() returns the player object. You can also
read window.easysignage directly.
Migrating from Broadsign:
EasySignage uses the same Broadsign key names
(display_unit_id, display_unit_resolution, frame_resolution, campaign_id,
display_unit_lat_long), so your field names carry over. Read them from the EasySignage
player object:
var panelVariables = EasySignage.read();
if (panelVariables) {
this.playerId = panelVariables.player_id || "";
this.screenId = panelVariables.display_unit_id || "";
this.latLong = panelVariables.display_unit_lat_long || "";
}
Warning:
window.BroadSignObjectwas removed. A creative that reads that global no longer receives player attributes. Update it to useEasySignage.read()orwindow.easysignage.
Load
easysignage.jsas a classic script (<script src="...">), not astype="module". A classic external script also runs under a strictContent-Security-Policy(script-src 'self') that would block inline scripts, and it works on older Tizen, webOS, and BrightSign webviews underfile://.
Available attributes
EasySignage delivers a focused, Broadsign-compatible set of attributes. The same value is exposed under both the native EasySignage key and its Broadsign alias, so a creative written for either platform can keep its field names.
Values are strings and are normally present as "" when unknown. The one exception is
display_unit_lat_long, which is present only when the device’s location is known.
| Native attribute | Broadsign alias | Description |
|---|---|---|
player_id | player_id | Unique player ID (GUID). Same key name as Broadsign. |
name | — | Display name of the player or screen. |
screen_id | display_unit_id | Screen (display unit) identifier. |
resolution | display_unit_resolution, frame_resolution | Screen resolution, for example 1920x1080. |
playlist_id | campaign_id | ID of the playlist this content is playing in (Broadsign’s nearest concept is the campaign). |
| — | display_unit_lat_long | Device location as a single "lat,long" string. Present only when the location is known. |
tags | — | Your custom labels as { key: value }. See Tags
. |
Data minimisation:
Custom apps are third-party code, so EasySignage exposes only the fields in the preceding table. Device identifiers (serial, device ID), OS, manufacturer and version details, precise separate coordinates, orientation, and the playlist and media names are not passed to your app.
Tags
Tags are the custom key:value labels you assign to your screens, for example zone:north,
store:42, or lang:ar. Use them to drive your own business logic. They arrive as an
object, plus a joined string:
var panel = EasySignage.read();
panel.tags.zone; // "north"
panel.tags.store; // "42"
panel.tags.all; // "zone:north,store:42" (comma-separated, key:value)
Tags are case-sensitive; prefer lowercase keys. Up to 20 tags are carried to the device. The complete set always exists in your EasySignage account.
Value rules
Every delivered field is a string.
Unknown values are present as an empty string
"". The key is never missing, so you can readpanel.namewithout checking that it exists first. The one exception isdisplay_unit_lat_long, which is present only when the device location is known.tagsis always an object, possibly empty.tags.allis the comma-separated string.Keep the package self-contained and use relative paths so it runs fully offline.
Example: show different content per screen
<script src="easysignage.js"></script>
<script>
var panel = EasySignage.read();
var zone = (panel.tags && panel.tags.zone) || "default";
// pick content based on the screen's zone tag
var banners = {
north: "north-promo.jpg",
south: "south-promo.jpg",
default: "generic-promo.jpg",
};
document.getElementById("hero").src = banners[zone] || banners.default;
// rotate the layout for portrait screens (resolution is "WIDTHxHEIGHT")
var res = (panel.resolution || "").split("x");
if (res.length === 2 && Number(res[1]) > Number(res[0])) {
document.body.classList.add("portrait");
}
</script>
Troubleshooting
Nothing shows up: Confirm the entry file is named
index.htmland sits at the top level of the.zip, and that every asset uses a relative path.The injected global is missing: A strict
Content-Security-Policyin your page can block the injected script. Use theeasysignage.jsreader, or read the URL query string, and your app still gets the values.The app fails on older devices: Keep your scripts as classic scripts, with no
type="module", so they run on older Tizen, webOS, and BrightSign webviews.Test locally first: Open
index.htmlin a browser with a query string appended, for exampleindex.html?player_id=TEST&display_unit_id=SC-1&resolution=1080x1920&tags.zone=north, to preview how your app reacts before you upload it.
Related articles
- Website Dynamic URL Variables : pass the same player attributes into an online URL.
- Upload Media : how to add media to EasySignage.