Build Your First Widget

You’ll build a follow alert that slides in from the right, plays a sound, and auto-hides after 5 seconds.

The HTML

<div id="follow-alert" class="alert hidden">
  <div class="text">
    <span id="username"></span> just followed!
  </div>
</div>

The CSS

.alert {
  position: fixed;
  top: 20px;
  right: 20px;
  background: rgba(15, 15, 25, 0.95);
  border: 1px solid #8B5CF6;
  border-radius: 12px;
  padding: 16px 24px;
  display: flex;
  align-items: center;
  gap: 12px;
  transform: translateX(120%);
  transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.alert.show {
  transform: translateX(0);
}
.alert.hidden {
  display: none;
}
#username {
  font-weight: 700;
  color: #A78BFA;
}
.text {
  color: #9ca3af;
  font-size: 16px;
}

The JavaScript

SBCanvas.ready(() => {
  const alertEl = document.getElementById('follow-alert');
  const usernameEl = document.getElementById('username');

  SBCanvas.activities.on('follow', (activity) => {
    usernameEl.textContent = activity.username;
    alertEl.classList.remove('hidden');

    // Trigger slide-in on next frame
    requestAnimationFrame(() => alertEl.classList.add('show'));

    // Play sound
    SBCanvas.sound('/sounds/follow.mp3', 0.8);

    // Auto-hide after 5 seconds
    SBCanvas.setTimeout(() => {
      alertEl.classList.remove('show');
      SBCanvas.setTimeout(() => alertEl.classList.add('hidden'), 400);
    }, 5000);
  });
});

Test It

Append ?preview=studio to your overlay URL, then open the browser console:
SBCanvas.preview.injectTestEvent('activity:follow', {
  username: 'CoolViewer42',
  provider: 'kick'
});
The alert should slide in, play the sound, and disappear after 5 seconds.

Making It Configurable

Add a control panel so streamers can adjust timing and color:
SBCanvas.panel.register({
  fields: [
    { key: 'duration', type: 'slider', label: 'Display Time (s)', min: 2, max: 15, default: 5 },
    { key: 'accent_color', type: 'color', label: 'Accent Color', default: '#8B5CF6' }
  ],
  actions: [
    { name: 'test', label: 'Test Alert' }
  ]
});

SBCanvas.on('panel:field_change', ({ key, value }) => {
  if (key === 'accent_color') {
    alertEl.style.borderColor = value;
  }
});

SBCanvas.panel.onAction('test', () => {
  SBCanvas.preview.injectTestEvent('activity:follow', {
    username: 'TestUser',
    provider: 'kick'
  });
});

Adding Persistence

Track total follow count that survives restarts:
SBCanvas.ready(async () => {
  let count = (await SBCanvas.store.get('totalFollows')) || 0;

  SBCanvas.activities.on('follow', async () => {
    count++;
    await SBCanvas.store.set('totalFollows', count);
  });
});

Next Steps

Custom Fields

Make your widget fully configurable

Media Sequences

Chain animations, sounds, and delays

Platform Detection

Adapt behavior per platform

Persistent Store

Save data across sessions