Architecture

Data Flow

Events travel from the streaming platform to your widget code in four steps:
// 1. Viewer does something on Kick/Rumble/Blaze
//    (follows, subs, tips, sends chat)

// 2. StreamBot backend normalizes into a typed Activity:
{
  id: "abc123",
  type: "follow",
  provider: "kick",
  username: "CoolViewer42",
  timestamp: 1716000000000,
  raw: { /* original platform payload */ }
}

// 3. WebSocket (Socket.IO) delivers to your overlay in real-time

// 4. Your widget receives it:
SBCanvas.activities.on('follow', (activity) => {
  alert.textContent = `${activity.username} followed!`;
});

Connection Lifecycle

// Overlay loads in OBS Browser Source
// → connects to StreamBot via Socket.IO
// → authenticates with OVERLAY_TOKEN embedded in the URL
// → receives session data (platform, channel, goals, emotes)
// → SBCanvas.ready() fires
// → events start flowing

SBCanvas.ready(() => {
  console.log(SBCanvas.platform.provider);   // 'kick'
  console.log(SBCanvas.platform.channelName); // 'MyChannel'
  console.log(SBCanvas.state.isConnected);    // true
});

Auto-Reconnect

The transport layer handles disconnects automatically. If the WebSocket drops:
  1. Reconnects with exponential backoff
  2. Re-authenticates with the same token
  3. Replays any queued store writes
  4. Resumes event delivery
Your widget code doesn’t need to handle reconnection logic.

Components

ComponentWhat It Does
TransportSocket.IO connection with auto-reconnect and heartbeat
Event BusRoutes events to widget listeners (on, once, off, after)
Activity NormalizerConverts raw Kick/Rumble/Blaze payloads into typed Activity objects
Queue ManagerSequences alert presentation by priority, prevents overlap
StorePersistent key-value storage with offline write queue
Service RegistryBackground worker lifecycle (init, dispose, event forwarding)
Session RuntimeChannel state, goals, emotes, field data

Widget Sandbox

Widgets run in a restricted environment:
// Network: proxied through backend (no direct fetch/XHR)
const res = await SBCanvas.http('https://api.example.com/data');

// Timers: tracked and auto-cleaned on unload
SBCanvas.setTimeout(fn, 5000);
SBCanvas.setInterval(fn, 1000);

// Cross-widget messaging: named channels only
const ch = SBCanvas.channel('score');
ch.publish({ points: 42 });

// Sanitization: built-in XSS protection
const safe = SBCanvas.sanitize.html(userInput);