AI OVERLAY SPEC

Build HypeTally OBS overlays with AI

This page is the system contract for custom overlays. Paste a short dashboard prompt into any AI chat, point it here, and paste the HTML it returns into the overlay editor.

Add ?slug=mychannel (your link name from the dashboard) to personalize the API URLs.

Bar layout: Compact Camera Bar · canvas 920 × 280

Like Counter 920 × 120Member Ticker 920 × 48Member Roster 920 × 48Subscriber Counter 920 × 120Live Chat 360 × 640Discord Voice Queue 920 × 280Discord On Air 420 × 120Twitch Sub Counter 920 × 120Twitch Sub Ticker 920 × 48Twitch Follower Counter 920 × 120Twitch Follow Ticker 920 × 48Twitch Bits Ticker 920 × 48Twitch Raid Alert 720 × 160Twitch Hype Train 640 × 96Twitch Stream Goal 920 × 120Donation Alerts 720 × 200

Discord Voice Queue

Waiting-room order for call-in streams managed by HypeTally.

  • OBS URL https://hypetally.com/overlay/your-slug/queue
  • Data API https://hypetally.com/api/your-slug/queue
  • Selected bar layout Compact Camera Bar
  • Recommended size 920 × 280

GET /api/{slug}/queue — { queueLength, queueFrozen, queue[{ position, displayName, avatarUrl? }], pollingIntervalMillis, paused?, awaitingBot?, subscriptionRequired?, discordNotLinked? }

Discord Voice Queue requirements

  • Show Voice Queue title, waiting count, optional frozen badge, ordered rows, and Up next.
  • Highlight position #1. Avatars optional.
  • Schedule every next queue poll from pollingIntervalMillis. Handle paused / awaitingBot / subscriptionRequired / empty waiting room.
  • Mods manage the queue in Discord — this overlay is display-only.

Editor-ready layers

Structure HTML so the visual editor can import separate layers (panel, counter, progress bar, ticker) instead of one locked embed. The dashboard AI prompt asks models to follow this section.

Editor-ready queue panel

  • Root: .QueueRoot, .QueueShell, or .QueuePanel.
  • List region: .QueueList or #QueueList with data-ht-bind="queue" if using bind markers.
  • Optional count/frozen: data-ht-bind="queueLength" and data-ht-bind="queueFrozen".
  • Valid data-ht-bind values: likeCount, likeGoal, likeFill, likePercent · subCount, subGoal, subFill, subPercent · members · queue, queueLength, queueFrozen · onAir · twitchRaid · twitchHypeTrain · donationAlert, donationEvents, donationGoal, donationGoalFill, donationGoalPercent, donationTotal. Map live elements to these (e.g. id="SubCurrent" with data-ht-bind="subCount").

Minimal shape: <div class="QueueRoot"><div class="QueueList" id="QueueList"></div></div>

All overlay types

  • Put layout styles in a <style> block using the class names below — the editor reads CSS by selector when importing layers.
  • Use separate DOM nodes for shell chrome vs live data (counter, fill bar, ticker track, queue list). Avoid one wrapper that only draws everything with background-image or a single SVG.
  • Size the root bar/panel to the target canvas width and height from this page.
  • Use only the documented data-ht-bind names — custom bind strings are ignored by the editor runtime.
  • Build API URLs as relative paths: `/api/{slug}/…` (same origin as the overlay). Never hardcode ngrok or trycloudflare URLs.
  • Apply to canvas, then Break into layers — counters, fills, and percent become editable HypeTally widgets; shell chrome stays as clip layers.

Break into layers (required)

Every overlay is split into editable layers in the visual editor. AI-generated HTML must follow these rules so counters and progress bars do not stack on top of duplicate text.

  • The streamer will always use Break into layers — HTML must decompose into separate shell clips plus HypeTally counter/progress widgets without overlapping duplicate text.
  • Use exact documented class and id names only. Do not invent aliases (.LikeSep, .LikeLabel, .LikeCounts) — use .LikeSlash, .LikeEyebrow, .LikeStats instead.
  • Live bind targets must be separate elements: #LikeCurrent, #LikeGoal, #LikeFill, #LikePercent (likes) or #SubCurrent, #SubGoal, #SubFill, #SubPercent (subs). Each needs data-ht-bind on the same node.
  • Wrap count + slash + goal in .LikeStats (likes) or .SubStats (subs). Keep #LikeCurrent and #LikeGoal as child spans — decompose merges them into one counter widget; do not rely on wrapper text-shadow.
  • Put glow, text-shadow, and large font-size on #LikeCurrent / #SubCurrent and #LikePercent / #SubPercent — not on parent wrappers (.LikeStats, .LikePercentWrap).
  • Progress track: .LikeProgressWrap > .LikeTrack > #LikeFill. The track shell becomes a clip layer; #LikeFill becomes the animated progress widget.
  • Optional percent badge: wrap #LikePercent in .LikePercentWrap with a static label (.LikePercentTag). Do not duplicate percent text outside #LikePercent.
  • Optional status copy: .LikeStatus or #LikeStatus for Awaiting live / Paused — separate from #LikePercent.
  • Decorative chrome (.LikeIcon, .LikeBrand, .LikeEyebrow, glass .LikeBar shell) must be separate DOM nodes, not CSS on bind targets.
  • Flat HTML in the document body — no bundlers, no JSON-in-script templates. Markup must exist before any script runs so the editor can measure layers.
  • API paths: `/api/{slug}/likes` (or subs/members/etc.) with `{slug}` or the streamer slug from the prompt — never a hardcoded channel name.

Global rules

  • One self-contained <!DOCTYPE html> document with inline HTML, CSS, and JS only.
  • Transparent page background — only the overlay chrome should be visible in OBS.
  • No frameworks, npm packages, bundlers, or external JS CDNs. Google Fonts CSS is OK.
  • Never embed API keys, tokens, or private credentials. Overlay APIs are public and slug-scoped.
  • Poll cadence is controlled by HypeTally. Always schedule the next request from pollingIntervalMillis on the last data API response. Do not invent intervals or drive polling from config poll*Ms fields.
  • Graceful errors: keep the last good UI on screen if a poll fails; always reschedule the next poll in finally.
  • Use a ~15s fetch timeout (AbortController) so a hung request cannot stall the loop.
  • Match the streamer’s selected overlay design pack and canvas size shown on this page.
  • Promoted preset overlays (dashboard bar designs) use the same class names as the examples below — that is the reference HTML for editor-ready imports.
  • Structure every overlay for Break into layers in the visual editor (not a single locked HTML embed). Use only the documented class and id names in the Editor-ready layers section.
  • Paste the finished HTML into the overlay editor HTML panel, Apply to canvas, Break into layers, then Save & Publish. OBS Browser Source URLs stay the same.

Public APIs (no keys)

All endpoints are JSON, cache: no-store friendly, and scoped to your link name. Never ask the streamer for YouTube or Discord secrets inside overlay code.

GET /api/your-slug/config

{ likeGoal, subGoal, subsDisplayMode, twitchSubsDisplayMode, twitchFollowsDisplayMode, twitchFollowGoal, memberScrollSeconds, pollingEnabled, apis } — display/settings only; ignore pollLikesMs/pollSubsMs/pollMembersMs/pollQueueMs for overlay poll cadence

GET /api/your-slug/likes

{ likeCount, likeGoal, pollingIntervalMillis, paused?, awaitingLive?, pollingEnabled? }

GET /api/your-slug/members?pageToken=...

{ newMembers[{ id, displayName, profileImageUrl?, memberLevelName?, isUpgrade?, isGift? }], tickerMembers[] (this-stream names, last 40 — hydrate overlay on reload), nextPageToken, pollingIntervalMillis, paused?, awaitingLive? }

GET /api/your-slug/members-roster

{ newMembers[{ id, displayName, memberLevelName?, priceLabel?, totalMonthsAsMember?, milestoneLabel? }] — full CSV roster list, replaced every poll, pollingIntervalMillis (~60000), paused?, pollingEnabled? }

GET /api/your-slug/subs

{ subscriberCount, subsGained, subGoal, subsDisplayMode: "total"|"gained", pollingIntervalMillis, paused?, awaitingLive? }

GET /api/your-slug/queue

{ queueLength, queueFrozen, queue[{ position, displayName, avatarUrl? }], pollingIntervalMillis, paused?, awaitingBot?, subscriptionRequired?, discordNotLinked? }

GET /api/your-slug/on-air

{ onAir[{ userId, displayName, avatarUrl?, durationMs?, joinedAtMs?, isBypass?, isSpeaking? }], showHostOnOverlay, hideHostOnOverlay, showTimerOnOverlay, pollingIntervalMillis, paused?, awaitingBot? }

GET /api/your-slug/twitch-subs

{ subscriberCount, subCount, subsGained, subGoal, subsDisplayMode: "total"|"gained", pollingIntervalMillis, paused?, awaitingLive?, twitchRequired?, subscriptionRequired? }

GET /api/your-slug/twitch-goal

{ goalLabel, current, target, percent, pollingIntervalMillis, paused? }

GET /api/your-slug/twitch-subs-ticker

{ newMembers[{ displayName, profileImageUrl? }], nextPageToken?, pollingIntervalMillis, paused?, awaitingLive? }

GET /api/your-slug/twitch-followers

{ followCount, subscriberCount, subCount, subsGained, followsGained, subGoal, subsDisplayMode: "total"|"gained", pollingIntervalMillis, paused?, awaitingLive?, twitchRequired?, subscriptionRequired? } — same shape as twitch subs; do NOT use followerCount/followerGoal/displayMode

GET /api/your-slug/twitch-followers-ticker

{ newMembers[{ displayName, profileImageUrl? }], pollingIntervalMillis, paused?, awaitingLive? }

GET /api/your-slug/twitch-cheers

{ newMembers[{ displayName, bits?, message? }], pollingIntervalMillis, paused?, awaitingLive? }

GET /api/your-slug/twitch-raid

{ active, from, viewers, pollingIntervalMillis }

GET /api/your-slug/twitch-hype-train

{ active, level, progress, goal, percent, pollingIntervalMillis }

GET /api/your-slug/donations

{ events[{ id, displayName, amountLabel, amountCents, message?, isNew, occurredAt, source? }], liveEvent?, newMembers[{ id, displayName }], goal{ currentCents, targetCents, label, percent }, pollingIntervalMillis, websocketPath, paused?, donationsRequired?, subscriptionRequired? } — WebSocket pushes include source (stripe|youtube|twitch|…) on event; popup shows all sources, ticker/goal use Stripe events[] only

Polling behavior

  • Poll cadence is set by HypeTally. Always schedule the next request using pollingIntervalMillis from the last likes/members/subs/queue/on-air response (fallback ~5s / 30s only if the field is missing).
  • Do not drive overlay polling from /config pollLikesMs, pollMembersMs, pollSubsMs, or pollQueueMs — those are dashboard/software settings.
  • If paused, awaitingLive, or pollingEnabled: false: keep the last good UI and recheck every pollingIntervalMillis || 30000.
  • Always schedule the next poll in a finally block.
  • Use ~15s fetch timeouts so a hung request cannot stall the loop.

Where to paste the result

  1. Open the HypeTally dashboard → Overlays → copy the prompt for your overlay type (YouTube, Discord, or Twitch).
  2. Open the matching editor tab, or use the Spec link next to Copy Prompt.
  3. Open the HTML editor, paste the full document, Apply to canvas if you want layers.
  4. Save & Publish — your OBS Browser Source URL does not change.

Open dashboard overlays