Logo
overlay platform
Overlay Studio Templates
Overlay Studio TemplatesWhat are overlay templates?Template RepositoryQuick startRepository structureCreating a templateThe file index.htmlThe file manifest.jsonLocal assets and security restrictionsSupported assetsIntentionally unavailable capabilitiesOther unavailable or forbidden capabilitiesProduction reviewDeveloping and testing a templateDesign guidelinesTest updates locallyPublishing your templatesFrom design to live production

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:update event, 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:

  1. The HTML loads with the presentation and values authored in your template.
  2. The manifest tells Studio which controls to display and supplies their initial values.
  3. Studio initially sends the effective values: either persisted session values or its merge of the manifest defaults.
  4. Later operator or API changes arrive as incremental nebular-overlay:update events.
  5. The template applies only the supplied fields to its DOM, styles, animations or internal state.
  6. 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 values payload as a JSON object rather than validating every field against the manifest.

  • The nebular-overlay:update event 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:

  • type determines the kind of value and the Studio control used to edit it.
  • label is the human-readable name shown to the operator.
  • default is 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,
}
  • cols and rows describe the preferred editor grid for the choose and multi widgets.
  • With responsive: true, Studio may adapt the columns to the available width.
  • icon optionally decorates the choices with a Font Awesome icon available in Studio.
  • Keep the values in default consistent with the types used in choose.
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.

  • default is the initial array of records offered by Studio.
  • meta defines the shape and controls of each record.
  • max limits 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 .js files are intentionally not served.
  • Avoid unnecessary images and fonts. Most templates need only index.html and, 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 textContent over innerHTML.
  • Do not include large assets. Each served file is limited to 5 MiB.
  • Ensure manifest.json is 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

  1. Create or modify a template locally.
  2. Make its initial HTML state match the defaults in manifest.json.
  3. Exercise complete and partial nebular-overlay:update events.
  4. Validate the repository and visually inspect the intended render resolution.
  5. Commit and push the tested version.
  6. Contact us to request review of the new or updated templates.