Overlay Studio Templates
What are overlay templates?
Overlay templates are ordinary, self-contained web pages designed to be rendered over live video using Nebular Overlay Studio. They may expose configurable values that can be edited in Studio or updated through the Overlay API while a live video session is running.
Template Repository
To simplify deployment, all templates for your organization live in a repository under your control.
Your design team or agency can create, test and maintain templates in this repository independently from the Overlay Server.
When a tested change is committed, your template library can be synchronized with the production Overlay Servers.
If enabled for your account, synchronization can also be requested through the API.
The repository contains presentation code and metadata only. Overlay sessions, saved parameter values, credentials and server configuration do not belong here.
Quick start
Refer to Overlay Studio SDK to learn how to
- quickly initialize a new repository for your organization and install some example templates.
- preview templates using the Preview Tool
- install the Codex Skill to assist in template creation using natural language
Repository structure
Create folders that reflect the categories you want to present in Studio. Templates may be organized at any folder depth, as shown below:
.
├── broadcast/
│ └── lower-third/
│ ├── index.html
│ └── manifest.json
├── gaming/
│ └── gta/
│ └── mission/
│ ├── index.html
│ └── manifest.json
└── sports/
└── tennis/
└── scoreboard/
├── index.html
└── manifest.json
The corresponding template IDs are:
broadcast/lower-third
gaming/gta/mission
sports/tennis/scoreboard
Any directory containing index.html is treated as a template. It is therefore a leaf in the template tree: do not place other templates beneath it.
Creating a template
Create a directory containing both required files:
category/template-name/
├── index.html
└── manifest.json
Names may contain letters, numbers, hyphens and underscores. Use relative paths for assets belonging to the template.
The file index.html
This is the web page that will be rendered over the live video stream.
- The page should have a transparent background and completely fill the render surface.
- It listens for the
nebular-overlay:updateevent, which carries values supplied by an operator or API client, such as lottery numbers, match results or chat messages. - The template decides how those values modify the page. In the following example, it changes some text and its colour.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Example Overlay</title>
<style>
* {
box-sizing: border-box;
}
html, body {
width: 100%;
height: 100%;
margin: 0;
overflow: hidden;
background: transparent;
}
#title {
color: #ff9818;
}
</style>
</head>
<body>
<div id="title">Hello</div>
<script>
const title = document.getElementById("title");
// Every event is an incremental update. Change only the values that
// are present and leave all other presentation state untouched.
window.addEventListener("nebular-overlay:update", ({detail}) => {
if (detail.content?.title != null) {
title.textContent = detail.content.title;
}
if (detail.style?.accent != null) {
title.style.color = detail.style.accent;
}
});
</script>
</body>
</html>
How live updates reach the page
When an operator changes the displayed overlay through Nebular Overlay Studio—or an application sends an API request—the page receives a custom event:
window.addEventListener("nebular-overlay:update", ({detail}) => {
// Apply the fields supplied in detail.
});
Updates are incremental. An event may contain one value, several values or every declared value; the distinction is irrelevant to the template. Always test whether a field was supplied and preserve the current state of fields that are absent.
The HTML is the template's presentation baseline. Put the values and visual state that should appear before any update directly in the HTML. This also makes the template render sensibly when opened directly during development.
The manifest defaults are the initial values offered by Studio. They should normally agree with the values authored in the HTML.
Complex templates are free to maintain whatever internal state they need: arrays, maps, animation queues, board state, timers or derived values. They must only honour the incremental-update contract and leave unrelated state untouched.
The complete flow is:
- The HTML loads with the presentation and values authored in your template.
- The manifest tells Studio which controls to display and supplies their initial values.
- Studio initially sends the effective values: either persisted session values or its merge of the manifest defaults.
- Later operator or API changes arrive as incremental
nebular-overlay:updateevents. - The template applies only the supplied fields to its DOM, styles, animations or internal state.
- The Overlay Server merges the changes into the session's persisted values.
Requesting a frame after a static change
The Overlay Server normally detects visual changes and captures frames automatically. Some very small or entirely static DOM changes may not immediately produce a frame, however. A template can explicitly notify the server after applying such a change:
title.textContent = "Updated title";
window.invalidate?.();
In general you should not normally need to use it. It is provided as a safeguard in case you experience visual glitches.
The file manifest.json
Purpose
The manifest describes the dynamic values presented to an operator—for example, sports results, lottery numbers or chat messages. It may also provide concise operating instructions for the template.
Nebular Overlay Studio uses this description to create the appropriate interactive controls automatically. The same values can also be updated programmatically through the Overlay API.
Studio creates controls only for parameters declared in
manifest.json.API callers should follow the same parameter contract, although the API accepts the
valuespayload as a JSON object rather than validating every field against the manifest.The
nebular-overlay:updateevent carries changes while the live session is running.
Operator instructions
The optional top-level instructions field explains how the template should be prepared or operated in Studio:
{
"instructions": "Build the question deck in Edit mode. During the broadcast, select a question and press Update to display it.",
"meta": {
"control/current": {
"type": "number",
"label": "Question to show",
"default": 1
}
}
}
Studio displays this text to the operator alongside the template controls. Keep it short, practical and focused on actions that may not be obvious from the field labels—for example, which values are prepared before going live, which controls are used during play, and whether an animation advances automatically.
instructions is manifest documentation, not a template parameter. It is not rendered as a control, included in the session values or delivered in a nebular-overlay:update event. Values intended for the page must still be declared inside meta.
Manifest for the preceding index.html
The following manifest defines the two values used by the example above: the title text and its accent colour.
{
"instructions": "Enter the title and choose its accent colour, then press Update.",
"meta": {
"content/title": {
"type": "string",
"phase": "play",
"label": "Title",
"default": "Hello from Nebular"
},
"style/accent": {
"type": "color",
"phase": "edit",
"label": "Accent colour",
"default": "#ff9818"
}
}
}
Each entry in the meta object describes one parameter. Its key is the parameter path, while its object contains at least three fields:
typedetermines the kind of value and the Studio control used to edit it.labelis the human-readable name shown to the operator.defaultis the initial value offered by Studio.
Some parameter types accept additional fields, as described below.
Edit and play phases
The optional phase field helps keep the operator interface focused when a template has many parameters. It accepts two values:
"edit"marks preparation and design values normally configured before going live, such as colours, titles, question collections or a fleet layout."play"marks operational values changed during the live session, such as scores, the current question, called numbers or player shots.
Studio provides an Edit/Play switch and hides fields belonging exclusively to the other phase. A parameter with no phase remains available in both modes.
{
"content/title": {
"type": "string",
"phase": "edit",
"label": "Game title",
"default": "Friday Night Quiz"
},
"control/current": {
"type": "number",
"phase": "play",
"label": "Question to show",
"default": 1
}
}
This distinction is purely presentational. phase does not impose permissions, freeze values, alter API behavior or change what reaches the template. Updates remain incremental regardless of the phase currently selected in Studio.
Parameter paths
A parameter may have a simple name such as clock, or a slash-separated path that groups related values. For example, content/title is delivered to the page as:
{
content: {
title: "Hello from Nebular"
}
}
Grouping becomes especially useful for related entities. A tennis scoreboard could receive:
{
"player1": {
"name": "Carlos Alcaraz",
"sets": 1,
"games": 3,
"points": 15
},
"player2": {
"name": "Jannik Sinner",
"sets": 1,
"games": 3,
"points": 30
}
}
The corresponding manifest keys would be player1/name, player1/sets, player1/games, player1/points, and the equivalent player2/... paths.
Parameter types
Each entry in meta selects a Studio widget through its type. The widget determines how the operator edits the field and the shape of the resulting value. Types are available at the top level and inside the meta of a set.
Options shared by parameter definitions
The containing record reads the following options for every widget:
| Option | Meaning |
|---|---|
type |
Widget to render. When omitted or unknown, Studio falls back to a string input. |
label |
Caption shown above the control. When omitted, Studio derives a readable label from source or the parameter path. |
default |
Initial value offered by Studio when no persisted value exists at the effective path. |
source |
Overrides the parameter path used to read and write the value. This allows two differently presented controls to share one underlying property. |
phase |
Displays the field in "edit" or "play" mode. With no phase, it appears in both. This is presentational only. |
size |
Studio grid width for this control, from 1 (smallest) to 12 (full row width). |
break |
When true, starts the following parameter on a new editor row. |
{
"content/player": {
"type": "string",
"phase": "edit",
"label": "Player",
"default": "John Carter"
},
"content/playPlayer": {
"type": "string",
"phase": "play",
"label": "Player",
"source": "content/player",
"readonly": true
}
}
Both controls address content/player; content/playPlayer is only a distinct manifest key required to describe the second presentation. If multiple definitions share a source, give them compatible defaults.
Supported widgets
| Type | Purpose | Common options |
|---|---|---|
string |
Free text, such as a title, player name or message | default, pattern, maxLength, hint, readonly |
number |
A numeric value, such as the number of goals scored | default, min, max, hint, readonly |
range |
A numeric value displayed in Studio as a slider | default, min, max, step |
date |
A local date-and-time picker stored as a Unix epoch in milliseconds | default, hint, readonly |
color |
A colour selected through Studio's colour picker | default, noalpha, remove, caption |
choose |
One value selected from a fixed set | default, choose, icon |
select |
One value selected from a dropdown | default, choose |
multi |
Multiple values selected from a set, such as called bingo or lottery numbers | default, choose, cols, rows, responsive |
set |
An editable array of structured records, such as questions, answers, players or poll options | default, meta, max, |
selector |
A one-based position selector for an item in a set |
default, set, include |
action |
A button that emits the current timestamp as a trigger | text, icon, hint |
reset |
A button that assigns predefined values to several parameter paths | text, icon, hint, values |
Every value-producing widget also accepts the shared default option. Choose a default whose shape agrees with the widget: a scalar for string, number, range, color, choose and select; an array for multi and set.
Text and numeric controls
string renders a single-line text input. hint becomes its placeholder and maxLength limits the number of entered characters. number renders a numeric input whose browser controls honour min and max. For either type, readonly: true replaces the input with static text while retaining the same underlying value.
range renders a slider. Its defaults are min: 0, max: 100 and step: 1 when those options are omitted.
{
"content/title": {
"type": "string",
"label": "Title",
"hint": "Enter an on-air title",
"maxLength": 80,
"default": "Live now"
},
"style/opacity": {
"type": "range",
"label": "Opacity",
"min": 0,
"max": 1,
"step": 0.05,
"default": 1
}
}
Selection controls
choose displays one button per entry and stores the selected scalar. For example, a tennis-points selector can be declared as:
{
"type": "choose",
"label": "Player one points",
"choose": ["0", "15", "30", "40", "A"],
"default": "0"
}
select presents the same kind of single selection as a native drop-down. Each entry in choose may be a string or an object containing a stored value and a displayed caption:
{
"timer/status": {
"type": "select",
"label": "Clock",
"hint": "Choose the clock state",
"choose": [
{"value": "running", "caption": "Running"},
{"value": "paused", "caption": "Paused"}
],
"default": "paused"
}
}
Use string values for select; native select controls return their selected value as text.
multi displays a grid and stores an array containing every selected entry. It is suitable for called balls, board coordinates, completed items or any other fixed collection in which several values may remain active simultaneously:
{
"type": "multi",
"label": "Called numbers",
"choose": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
"default": [3, 8],
"cols": 5,
"responsive": true,
}
colsandrowsdescribe the preferred editor grid for thechooseandmultiwidgets.- With
responsive: true, Studio may adapt the columns to the available width. iconoptionally decorates the choices with a Font Awesome icon available in Studio.- Keep the values in
defaultconsistent with the types used inchoose.
Colour controls
color opens Studio's colour picker. caption displays text inside the colour box, noalpha: true disables alpha-channel editing, and remove: true lets the operator clear the colour.
{
"style/accent": {
"type": "color",
"label": "Accent",
"caption": "Brand colour",
"noalpha": true,
"remove": false,
"default": "#ff9818"
}
}
Structured sets
The set type represents an array of objects. Its nested meta object describes the fields in each item, using the same parameter definitions as the top-level manifest. Studio lets the operator move through the items and add or delete them.
For example, this definition creates an editable question-and-answer deck:
{
"questions": {
"type": "set",
"label": "Questions",
"max": 10,
"container": "big",
"default": [
{
"question": "What is the largest ocean on Earth?",
"answer": "The Pacific Ocean"
},
{
"question": "Which element has the symbol Au?",
"answer": "Gold"
}
],
"meta": {
"question": {
"type": "string",
"label": "Question"
},
"answer": {
"type": "string",
"label": "Answer"
}
}
}
}
Updates deliver the corresponding value as an ordinary array of objects:
{
questions: [
{question: "What is the largest ocean on Earth?", answer: "The Pacific Ocean"},
{question: "Which element has the symbol Au?", answer: "Gold"}
]
}
The nested definitions are recursive: a field inside a set may use any supported type, including another set. Use recursion deliberately, because deeply nested editors quickly become difficult for an operator to navigate.
defaultis the initial array of records offered by Studio.metadefines the shape and controls of each record.maxlimits how many records the operator may create. When omitted, the set has no explicit item limit.container: "big"requests additional editor width for records containing several fields.
As with every update, a supplied set replaces the value of that parameter; an absent questions field leaves the template's current question collection untouched. An empty array is a supplied value and clears the collection.
Operator command buttons
action and reset are controls for common operational commands rather than ordinary data entry.
An action button writes Date.now() to its parameter path and immediately publishes that single change. This is useful for commands such as triggering a goal animation, advancing a visual effect or sounding a purely visual alert:
{
"action/goal": {
"type": "action",
"phase": "play",
"icon": "football-ball",
"text": "Goal"
}
}
The latest timestamp is persisted with the session. Treat it as a short-lived trigger rather than a historical event. If a template could be restored with an old action value, it may ignore stale timestamps:
const timestamp = detail.action?.goal;
if (typeof timestamp === "number" && Math.abs(Date.now() - timestamp) <= 10_000) {
showGoalAnimation();
}
A reset button applies every entry in its values object to the Studio editor. It does not publish automatically: the operator can inspect the resulting state and press Update to synchronize it with the live overlay.
Reset targets are canonical slash-separated parameter paths and may update several unrelated branches atomically when the operator subsequently presses Update:
{
"game/reset": {
"type": "reset",
"phase": "play",
"icon": "undo",
"text": "Reset round",
"values": {
"timer/seconds": 60,
"timer/status": "paused",
"game/correct": [],
"game/failed": []
}
}
}
Use Font Awesome 5 icon names that are available in Studio, without the fa- prefix—for example, "undo", "trash" or "football-ball".
Local assets and security restrictions
An overlay should render consistently during development and in production. To make that possible—and to prevent a template from unexpectedly communicating with other systems—templates run in a deliberately restricted environment.
The simplest rule is: keep everything the overlay needs inside its own directory.
broadcast/lower-third/
├── index.html
├── manifest.json
└── assets/
├── overlay.css
├── logo.webp
└── inter.woff2
Reference those files with relative URLs from index.html:
<link rel="stylesheet" href='assets/overlay.css'>
<img src='assets/logo.webp' alt="">
Supported assets
Supported local asset formats are:
- CSS:
.css - Images:
.jpg,.jpeg,.png,.webp - Fonts:
.otf,.ttf,.woff,.woff2
Additional remarks:
- Each served file is limited to 5 MiB.
- JavaScript must be written inline in
index.html; local.jsfiles are intentionally not served. - Avoid unnecessary images and fonts. Most templates need only
index.htmland, where appropriate, a small number of lightweight local assets. - Refer to the examples in this repository for complete working structures.
Intentionally unavailable capabilities
The rendering policy permits inline JavaScript, inline CSS and supported local files, but automatically blocks:
- External scripts, styles, fonts, images and, in general, outside communication.
- JavaScript connections to external services through
fetch, XHR, WebSocket or EventSource. As a rule of thumb, connections initiated from inside the page are forbidden. - Audio and video resources. Video and audio are already provided by the live stream; overlay media is not supported so that the incoming video signal remains smooth.
- Frames, IFrames, embedded objects, form submission and Web Workers.
- Popups or browser windows, including those that point to local resources.
In summary, a template cannot load libraries or assets from public or private CDNs, fetch private data directly or open its own WebSocket. All operator-facing dynamic values must be declared in the manifest and enter through nebular-overlay:update events. API clients must follow this contract.
Other unavailable or forbidden capabilities
- WebGL, canvas rendering, WASM modules and WebAudio.
- Access to devices or local resources such as cameras, microphones and the filesystem.
- Large or deeply nested DOMs.
- Complex JavaScript, especially tight compute loops.
Production review
Most restrictions above are enforced automatically, but rendering complexity is assessed separately.
New and updated templates are reviewed before they become available in production. A template that could place excessive load on the rendering system may need to be simplified before it can be approved.
Developing and testing a template
Design guidelines
- Design for a transparent render surface, normally 1920×1080.
- Use percentages, viewport units or responsive layout where appropriate.
- Keep animations bounded and inexpensive. The overlay capture frame rate is intentionally capped to preserve server performance.
- Avoid complex JavaScript. Ideally, use it only to modify displayed values and presentation state. Templates that require excessive processing may not be suitable for production rendering.
- Update existing DOM elements instead of rebuilding the entire document.
- Treat all supplied values as untrusted data; prefer
textContentoverinnerHTML. - Do not include large assets. Each served file is limited to 5 MiB.
- Ensure
manifest.jsonis a well-formed JSON file before updating your templates.
Test updates locally
Open index.html in a browser. The initial HTML should already show a useful default state. Then open the browser console and dispatch an update manually:
window.dispatchEvent(new CustomEvent("nebular-overlay:update", {
detail: {
content: {title: "Local preview"},
style: {accent: "#ff9818"}
}
}));
Try partial updates as well. This verifies that an omitted field does not accidentally reset another part of the overlay:
window.dispatchEvent(new CustomEvent("nebular-overlay:update", {
detail: {
content: {title: "Only the title changes"}
}
}));
Use an empty string to clear text and an empty array to clear a multiple-selection value. Both are supplied values and must therefore be applied. Do not interpret missing fields as a request to restore defaults.
Local testing verifies the page logic and visual design. The final check should still happen in Nebular Overlay Studio, where the template runs with the same security policy and update bridge used by the Overlay Server.
Publishing your templates
New and updated templates are reviewed before publication to ensure that they render correctly, comply with the security policy and do not adversely affect the Service.
Once you notify us that a version is ready, we will review it and make it available to your organization or explain any changes required before approval.
From design to live production
- Create or modify a template locally.
- Make its initial HTML state match the defaults in
manifest.json. - Exercise complete and partial
nebular-overlay:updateevents. - Validate the repository and visually inspect the intended render resolution.
- Commit and push the tested version.
- Contact us to request review of the new or updated templates.