320 lines
17 KiB
Markdown
320 lines
17 KiB
Markdown
# OBS overlays
|
|
|
|
Admins manage overlays at **Admin → OBS overlays**. Management pages use the
|
|
normal Lumi session, role checks, layout, forms, lists, modals, and confirmation
|
|
flow. Browser Source pages use a separate transparent document with no Lumi
|
|
navigation or authenticated website data.
|
|
|
|
## Media source behavior
|
|
|
|
Video sources render only their pixels. Browser controls, poster chrome, and
|
|
fallback backgrounds are never exposed in the public overlay. A video remains
|
|
hidden until the media element reports real playback and is hidden again after
|
|
end, stop, clear, or error. Playback controls stay in the Lumi inspector.
|
|
|
|
Audio sources are managed outputs rather than visual canvas objects. Their
|
|
play, pause, stop, seek, volume, mute, loop, test, and removal controls live in
|
|
the source list/inspector. Public overlays contain no visible audio UI, and old
|
|
audio position, size, layer, crop, alignment, opacity, and anchor fields are
|
|
ignored for compatibility.
|
|
|
|
## Editing an overlay
|
|
|
|
The editor uses the same basic model as OBS: scenes contain ordered sources, and
|
|
the selected source has a red box with resize handles. On a wide screen the
|
|
preview stays beside the settings; on smaller screens it moves above them.
|
|
|
|
The preview and clean OBS page now render one fixed, full-resolution canvas and
|
|
scale that finished canvas to the available display area. Embedded alert pages
|
|
therefore receive the same CSS-pixel viewport in the editor and in OBS instead
|
|
of switching responsive layouts when the management preview is smaller. Set the
|
|
OBS Browser Source width and height to the Lumi canvas dimensions shown in the
|
|
editor (normally 1920 by 1080).
|
|
|
|
- Drag a source to move it.
|
|
- Drag any corner or edge handle to resize it.
|
|
- Use arrow keys for small moves, or Shift plus an arrow for larger moves.
|
|
- Snapping uses canvas edges, center lines, and other source edges/midpoints.
|
|
- Hold Ctrl on Windows/Linux or Cmd on macOS to temporarily reverse the Snap
|
|
setting.
|
|
- Center, Fit to canvas, and Reset box provide familiar transform shortcuts.
|
|
|
|
Every source can use any corner, edge midpoint, or center as its anchor. Text
|
|
sources have separate horizontal and vertical alignment plus wrapping, shrinking,
|
|
clipping, single-line, and scrolling ticker behavior. The editor only shows fields
|
|
that apply to the selected source type.
|
|
|
|
## Private OBS links
|
|
|
|
Each overlay has one server-controlled URL. It follows the active overlay scene.
|
|
Each scene also has a fixed URL that always renders that scene. Both URL forms
|
|
contain 256-bit random bearer tokens instead of database IDs. Tokens are stored
|
|
encrypted for admin copy actions and as SHA-256 hashes for public lookup.
|
|
Regenerating a URL immediately revokes the old token.
|
|
|
|
Treat every Browser Source URL as a password. Only admins can view, copy, preview,
|
|
or regenerate it. Public state responses contain render data only; they do not
|
|
contain other URLs, tokens, OBS credentials, or website session data.
|
|
|
|
Browser Sources receive scoped live-change events through Lumi's existing event
|
|
service. The render page fetches a fresh authorized snapshot after each event and
|
|
after every reconnect, so temporary network loss does not require an OBS refresh.
|
|
|
|
## Scenes and sources
|
|
|
|
An overlay always keeps at least one scene. Admins can create, edit, duplicate,
|
|
order, enable, activate, and delete scenes. Current built-in source types are:
|
|
|
|
- Text
|
|
- Image URL
|
|
- Video / media URL
|
|
- Audio URL
|
|
- Lumi chat
|
|
- Website or alert overlay
|
|
|
|
Module instances can be created, edited, duplicated, ordered, enabled, and
|
|
deleted. New module types can call `registerOverlayModuleType(...)` in
|
|
`src/services/overlay-modules.js`; providers normalize their configuration and
|
|
select the safe built-in `text`, `image`, `video`, `audio`, `chat`, or `web` render type while the overlay
|
|
service retains the same instance model.
|
|
|
|
### Lumi chat
|
|
|
|
The built-in **Lumi chat** source combines messages already received by Lumi
|
|
from Twitch, YouTube, and Discord. Administrators can select any combination of
|
|
those services and optionally filter by channel name or ID. Channel filters
|
|
accept one value per line and may be prefixed with the service, such as
|
|
`twitch:cozycarnage` or `discord:community-chat`.
|
|
|
|
Chat sources support transparent source and message backgrounds, font family,
|
|
size, weight and whole-message alignment, name/text colors, avatars, badges, platform labels,
|
|
timestamps, message spacing, rounded corners, maximum visible messages,
|
|
top-or-bottom placement, timeout removal, and separate entry and exit
|
|
animations. Message cards fit their contents up to the source boundary rather
|
|
than filling its width. Entry and exit slides can travel left, right, up, or
|
|
down. Native Twitch emotes, global and channel BetterTTV emotes, and Discord
|
|
custom or animated emoji are displayed as images. BetterTTV channel and shared
|
|
emotes override a same-named global emote, matching normal channel behavior.
|
|
Discord GIF links, GIF attachments, GIFV embeds, and stickers are shown
|
|
as bounded inline media at roughly two text lines tall. The editor continuously
|
|
cycles several short and wrapped sample messages so both static appearance and
|
|
entry/exit behavior can be configured before the stream is live.
|
|
|
|
Lumi includes messages sent by its own connected bot accounts. Platform marks
|
|
are shown as icons, and chat badges are rendered as images. Twitch badge artwork
|
|
is resolved from Twitch's global and channel badge catalogs; a local image
|
|
fallback is used when a provider does not expose badge artwork. Twitch profile
|
|
images are resolved through the Twitch user catalog and cached to avoid a lookup
|
|
for every message. BetterTTV catalogs are likewise cached, and API failures fall
|
|
back to ordinary message text without interrupting chat delivery.
|
|
|
|
The optional hidden-user list accepts one rule per line in the form
|
|
`platform:username|user-id`. Twitch, YouTube, and Discord rules match only that
|
|
platform. A `lumi:internal-name|user-id` rule uses Lumi's linked identity model,
|
|
so one rule hides every connected platform identity belonging to that Lumi user.
|
|
Filtering is enforced before a message enters an overlay's scoped event stream
|
|
and is also checked by the renderer.
|
|
|
|
Advanced CSS is isolated to that chat source and is appended after the visual
|
|
settings. Stable selectors include `.chat`, `.message`, `.avatar`, `.content`,
|
|
`.meta`, `.platform`, `.author`, `.badge`, `.timestamp`, `.text`, `.emote`,
|
|
`.media-row`, and `.media`.
|
|
Messages arrive through the same token-scoped overlay event connection as scene
|
|
updates. Lumi keeps a small, short-lived in-memory replay window so a temporary
|
|
Browser Source reconnection can restore recent messages without writing chat
|
|
content to the database.
|
|
|
|
Every saved Lumi chat source also provides an **OBS chat dock** link to
|
|
administrators. Add it from **OBS → Docks → Custom Browser Docks**. This
|
|
standalone page uses that source's saved services, channel and hidden-user
|
|
filters, typography, colors, message details, limits, timeout, and custom CSS.
|
|
It intentionally ignores overlay position, source width/height, and entry/exit
|
|
animations. The page fills the available dock width, grows and scrolls like a
|
|
normal webpage, follows new messages while at the bottom, and leaves the scroll
|
|
position alone while an operator reviews older messages.
|
|
|
|
Treat a chat dock link as a password. It is signed and shown only to admins.
|
|
Replacing the overlay or scene private links also revokes affected dock links;
|
|
deleting or disabling the chat source makes its dock unavailable. Lumi also
|
|
revalidates credentials on open live-event streams and closes them after
|
|
revocation.
|
|
|
|
## OBS replay commands
|
|
|
|
The core `!clip [optional label]` command can save the active replay buffer
|
|
through either operational OBS connector while also requesting a clip from the
|
|
current streaming platform. Local OBS WebSocket uses `SaveReplayBuffer`. The
|
|
Browser Bridge uses OBS's native page binding and needs **Basic access to OBS**
|
|
or higher in Browser Source properties. The replay buffer must already be
|
|
running. Lumi selects one connected connector with an active replay buffer so a
|
|
setup with several overlays does not save duplicate replay files.
|
|
|
|
### Video and audio
|
|
|
|
Video sources support browser-playable media such as WebM and MP4; audio sources
|
|
support browser-playable formats such as Ogg, MP3, and WAV. Actual codec support
|
|
follows the Chromium/CEF build used by the browser or OBS.
|
|
|
|
Playback can start once when the overlay scene loads, loop continuously, or wait
|
|
for manual playback. Sources also provide volume, start offset, playback speed,
|
|
optional player controls, and a live **Restart playback** action. Video adds mute
|
|
and contain/cover/stretch sizing. Activating another Lumi scene removes the old
|
|
scene's media elements, which stops their playback; returning to the scene applies
|
|
its configured start behavior again.
|
|
|
|
Website overlays can play their own alert audio because their iframe receives
|
|
autoplay permission. To route video, audio, and website-alert sound through an
|
|
independent OBS mixer channel, enable **Control audio via OBS** in the Lumi Browser
|
|
Source properties, then configure monitoring/output in OBS as desired.
|
|
|
|
## Event alerts and sound alerts
|
|
|
|
Open an overlay and use **Event alerts** to connect a live service event to a
|
|
source. **Play media once** restarts an Audio or Video source and waits for that
|
|
playback to finish before the next alert for the same source begins. For a sound
|
|
alert, set the Audio source playback option to **Wait for manual playback**.
|
|
**Show source temporarily** keeps its Text or Image source hidden in clean OBS
|
|
output until an event arrives, displays it for the selected duration, and hides
|
|
it again. The editor still shows the source so it can be positioned normally.
|
|
|
|
The built-in event inputs are Twitch follows, incoming raids, subscriptions,
|
|
gifted subscriptions, and Discord member joins. Use **Connect Twitch events**
|
|
once as an administrator to grant the follower/subscription scopes. The token is
|
|
stored only on the server, refreshes automatically, and is never included in an
|
|
overlay page. Twitch events reconnect after temporary network loss. Discord
|
|
member joins use the existing bot connection and require the Server Members
|
|
Intent to be enabled for the bot in the Discord Developer Portal.
|
|
|
|
Use **Test alert** while either the pop-out preview or OBS Browser Source is open.
|
|
Lumi reports when no matching Browser Source is connected. An event only affects
|
|
the source when its scene is the one currently rendered; fixed scene links
|
|
therefore remain isolated. Plugins can add event types through the shared
|
|
`events.registerType(...)` and `events.emit(...)` API received during plugin
|
|
initialization.
|
|
|
|
### External website and alert overlays
|
|
|
|
Website sources support third-party browser overlays such as alert and chat
|
|
overlays. They run in a sandbox and do not receive the Lumi admin page or its
|
|
login data. The **Refresh now** action sends a live refresh command to every open
|
|
OBS copy of that source.
|
|
|
|
Automatic recovery is optional. When enabled it watches for a page that never
|
|
finishes loading, can check public website replies for HTTP errors, empty content,
|
|
unexpected content types, and obviously malformed pages, retries at 5, 15, and 45
|
|
second-style bounded intervals, and periodically reloads the source to avoid stale
|
|
content. Retry count and refresh interval are configurable. Local/private website
|
|
addresses are periodically refreshed but are not fetched by the Lumi server for
|
|
security; browser load-timeout recovery still applies.
|
|
|
|
Website sources support per-edge percentage cropping, zoom, and document-level
|
|
Website CSS. Lumi retrieves the public website document through a scoped,
|
|
revocable source URL and appends the stylesheet to its `<head>`, matching how OBS
|
|
injects Custom CSS after the main document loads. The proxied document runs in a
|
|
sandboxed opaque origin, receives no Lumi login/session data, does not expose the
|
|
overlay token in its location, and cannot use Lumi as a private-network proxy.
|
|
|
|
CSS injection is enabled by default and can target the website's own elements,
|
|
for example `body`, `.alert`, or provider-specific containers. Direct
|
|
compatibility mode is available for private/local pages and unusual sites that
|
|
cannot run through document injection; direct mode intentionally cannot change
|
|
cross-origin page content.
|
|
|
|
**Keep the website background transparent** is enabled by default. It protects
|
|
the page, body, and common application roots with a transparent background and
|
|
reapplies that protection when a late-loading alert application changes its DOM
|
|
or inline styles. It can be disabled for a website that intentionally needs an
|
|
opaque page background.
|
|
|
|
Lumi applies OBS's standard transparent Browser Source CSS to every clean render
|
|
page and shows the same copyable value in the Browser Source setup section:
|
|
|
|
```css
|
|
body { background-color: rgba(0, 0, 0, 0); margin: 0px auto; overflow: hidden; }
|
|
```
|
|
|
|
The Lumi document, canvas stage, embedded frame surface, and injected website
|
|
body are transparent by default. Administrators can extend or override the
|
|
default stylesheet per source just as they do in OBS Browser Source properties.
|
|
|
|
## Visual editing workflow
|
|
|
|
Existing sources use a draft preview workflow. Changing a field sends the draft
|
|
through the same server normalizer used for saving and updates the canvas after a
|
|
short delay without changing the stored source. Website URLs and CSS receive a
|
|
short-lived, access-controlled preview document, so their unsaved appearance is
|
|
also visible. Each changed card is marked as unsaved and offers **Save source**
|
|
or **Undo unsaved changes**.
|
|
|
|
The preview can switch between transparency checkerboard, dark, and light
|
|
backgrounds to expose unwanted opaque areas or low-contrast text. An optional
|
|
grid adds five-percent lines and canvas midpoints. Source save controls remain
|
|
sticky at the bottom of long settings panels, while drag, resize, keyboard
|
|
nudge, anchors, snapping, crop, zoom, and precise number fields remain available.
|
|
|
|
## OBS WebSocket
|
|
|
|
The connector is disabled by default. Enable OBS WebSocket in OBS, keep its
|
|
password enabled, and use the default local endpoint
|
|
`ws://127.0.0.1:4455` when Lumi runs on the streaming computer. If Lumi runs on
|
|
another trusted machine, use a private LAN/VPN address and firewall rules. Do not
|
|
port-forward or publicly expose OBS WebSocket.
|
|
|
|
The admin page can test the connection, read OBS scenes, change the current OBS
|
|
scene, and map OBS scene names to overlay scenes. Sync can run in either direction
|
|
or bidirectionally. Source tagging and a short outbound suppression window prevent
|
|
sync loops. Connector errors never stop overlay rendering, and optional reconnect
|
|
uses bounded exponential backoff.
|
|
|
|
OBS passwords use authenticated AES-256-GCM encryption derived from Lumi's stored
|
|
session secret. They are never returned by public APIs or rendered for
|
|
non-admins.
|
|
|
|
## OBS Browser Bridge
|
|
|
|
The optional **OBS Browser Bridge** uses the `window.obsstudio` bindings that OBS
|
|
injects into Browser Sources. It does not need an OBS WebSocket address or
|
|
password. Select the provider, save it, then load the private Lumi overlay URL in
|
|
OBS. The render page reports a heartbeat every five seconds and immediately after
|
|
relevant OBS events.
|
|
|
|
Use the minimum Page permission needed:
|
|
|
|
- **Read access to user information** reads the current scene, scene list,
|
|
transitions, canvas dimensions, output status, and Browser Source activity.
|
|
- **Advanced access to OBS** is needed only when Lumi should change OBS scenes.
|
|
- Full access is not requested or used by the current bridge.
|
|
|
|
Lumi elects one recently active Browser Source instance per overlay as the bridge,
|
|
targets commands to that instance, and deduplicates command IDs. Existing
|
|
scene-direction checks and outbound suppression prevent synchronization loops. If
|
|
the source stops reporting, Lumi marks the bridge as waiting instead of breaking
|
|
overlay rendering. Leave OBS's **Shutdown source when not visible** option off if
|
|
the bridge must remain available outside the source's active scene.
|
|
|
|
The **Apply detected OBS setup** action adds missing OBS scenes, maps same-name
|
|
Lumi scenes, adopts the detected OBS canvas size, and selects the current OBS
|
|
scene. It does not delete existing Lumi scenes. OBS Browser bindings do not expose
|
|
profile or scene-collection names, so those fields are explicitly reported as
|
|
unavailable; the OBS WebSocket provider can read them when that metadata is
|
|
required.
|
|
|
|
## Connector extension point
|
|
|
|
`src/services/overlay-connectors.js` owns the provider registry. A provider
|
|
creates a connector with these operations:
|
|
|
|
- `connect()` and `disconnect()`
|
|
- `status()`
|
|
- `listScenes()` and `getActiveScene()`
|
|
- `setActiveScene(sceneName)`
|
|
- `onSceneChanged(listener)` and `onClosed(listener)`
|
|
|
|
The reserved `trusted_lumi_client` provider is intentionally unavailable. A
|
|
future trusted desktop client can implement this interface to transport OBS state
|
|
and commands without changing overlay routes, mappings, or synchronization rules.
|
|
Plugins can register providers, module types, or future scoped access providers
|
|
through `global.lumiFrameworks.overlays`.
|
|
|
|
Run focused verification with `npm run verify:overlays`.
|