Skip to main content
Corvus Connection
All overlays

Overlay Studio

The Overlay Studio is a code editor built into the app for authoring your own OBS overlays. Write HTML, CSS, and JavaScript, or reach for SCSS and TypeScript, and see the result compile in a live preview as you type. Subscribe to live stream events with a typed SDK, then publish a single, self-contained file to drop into OBS.

How it works

The editor compiles your source in the browser and renders it into a sandboxed preview frame on every keystroke, so what you see matches what OBS will show. Plain HTML, CSS, and JavaScript need no toolchain at all; only SCSS and TypeScript load a compiler, and if one fails to load, the preview falls back to your raw source with the error surfaced so it never goes blank.
HTML, CSS, and JavaScript
Plain authoring with no toolchain. The fastest path to a working overlay.
SCSS
Compiled to CSS with dart-sass in the browser.
TypeScript
Compiled to JavaScript with esbuild, type-checked against the SDK contract.

Bring a library

Pick from a built-in catalog of CSS frameworks, retro themes, an animation library, and an icon font. The studio folds only what you select into the published file, so the artifact stays small and self-contained. Everything except Tailwind compiles fully offline.
Tailwind CSS v4CSS framework
Utility classes, compiled to static CSS at edit time. Needs a network connection.
Bootstrap 5CSS framework
The full Bootstrap component and utility set.
Fluent UI Web ComponentsCSS framework
Microsoft Fluent web components, bundled into the artifact.
NES.cssRetro theme
An 8-bit, NES-style theme.
98.cssRetro theme
A Windows 98 look.
XP.cssRetro theme
A Windows XP look.
Cybercore CSSRetro theme
A neon cyberpunk theme.
FantaCSSRetro theme
A classless fantasy theme.
anime.js v4JS library
An animation library, tree-shaken into the artifact.
RPG AwesomeIcon font
A fantasy and tabletop icon font, inlined as base64.

Live-data SDK

Opt into live data and the app inlines a typed runtime, window.StreamPulse, into your overlay. It connects to the overlay hub, joins your ?broadcasterId group, and dispatches each push to your handlers, so you never hand-wire a socket. Register your subscriptions at the top level of your script and the runtime auto-starts on the next tick.
The runtime is present only in live-data builds, so guard access with if (window.StreamPulse), or rely on auto-start by registering handlers up front.

Imperative API

Subscribe with a typed handler, or consume an event as an async stream.
// Subscribe with a typed handler.
StreamPulse.on("overlayAlert", (a) => {
  console.log(a.kind, a.displayName);
});

// Or consume an event as an async stream.
for await (const msg of StreamPulse.events("overlayChatMessage")) {
  console.log(msg.displayName, msg.message);
}

Class API

Prefer classes? Extend OverlayBase, tag methods with @handles, and compose the WithDom and WithLogging mixins. Construct it and the runtime wires the tagged methods for you.
class MyOverlay extends WithLogging(WithDom(OverlayBase)) {
  @handles("overlayAlert")
  onAlert(a: AlertOverlayPayload) {
    const el = this.byId("count");
    if (el) { el.textContent = a.displayName; }
    this.log.info("alert: " + a.kind);
  }

  override onConnected() { this.applyTheme(); }
}

new MyOverlay();

Events and payloads

Five events are available. Property names are camelCase, matching what the hub pushes. A ? marks an optional field.
overlayAlertAlertOverlayPayload
A follow, subscription, gift sub, cheer, raid, or donation.
kind: string
One of follow, subscription, subscriptionGift, cheer, raid, donation.
displayName: string
Name of the follower, subscriber, cheerer, and so on.
amount: string?
Pre-formatted amount, for example "100 bits". Absent for follows.
message: string?
Optional sub-line, for example a cheer or donation message.
overlayChatMessageChatOverlayPayload
One chat line. Render message with textContent, never innerHTML.
displayName: string
Chat author display name.
message: string
The message text.
color: string?
Author color as #rrggbb, when Twitch supplied one.
isBroadcaster: boolean
Author role flags.
isModerator: boolean
Author role flags.
isVip: boolean
Author role flags.
isSubscriber: boolean
Author role flags.
pronouns: string?
Pronoun display, for example she/her, when resolved.
overlayNowPlayingNowPlayingOverlayPayload
The current Spotify track. Interpolate the bar between pushes from progressMs and durationMs.
isPlaying: boolean
Whether a track is playing.
title: string?
Track title.
artist: string?
Track artist.
albumArtUrl: string?
An https Spotify-CDN album-art URL, when available.
progressMs: number
Playback position in milliseconds.
durationMs: number
Track length in milliseconds.
weatherAlertWeatherAlertOverlayPayload
One push carrying one or more weather-alert items for a location.
location: string
The location the alerts apply to.
alerts: WeatherAlertOverlayItem[]
headline, event, severity, areas, and expires per item.
weatherAstronomyWeatherAstronomyOverlayPayload
Sun and moon state, sunrise and sunset, and moon phase for a location.
location: string
The location this astronomy applies to.
isSunUp: boolean
Whether the sun is currently up.
isMoonUp: boolean
Whether the moon is currently up.
sunrise: string?
Sunrise time, when known.
sunset: string?
Sunset time, when known.
moonPhase: string?
Moon phase name, when known.
moonIllumination: number
Moon illumination as a percentage from 0 to 100.

Publish

Publishing compiles everything ahead of time into one file with a strict, per-artifact content security policy. The published overlay runs no WebAssembly and no eval, and pulls nothing from a CDN, so it loads fast in OBS and keeps working offline. Add it as a Browser source the same way you would a built-in overlay, with your ?broadcasterId.