TokElementsDOCS
WIDGET SDK / V1
01

Pagsisimula

1
Isulat ang markup at style

Ang HTML at CSS tabs ang gumuguhit ng widget mo. Transparent ang box at nakalutang ito sa ibabaw ng video — huwag kailanman mag-paint ng full opaque background.

2
Mag-react sa events

Sa JS tab, mag-subscribe gamit ang TE.on('gift', fn). Naka-load na ang window.TE — walang imports, walang setup.

3
I-test at i-add

Gamitin ang Simulate o ang Fire buttons para mag-preview offline, tapos Add to overlay — o hayaang si Claude ang gumawa nito via MCP.

Ang una mong widget — isang gift shout-out· i-edit ang JS, nagre-react live ang preview
var box = document.getElementById('box');
TE.on('gift', function (ev) {
  box.textContent = ev.user.name + ' sent ' + ev.gift.name + '!';
  box.classList.remove('pop'); void box.offsetWidth; box.classList.add('pop');
});
Live preview
02

I-connect si Claude (MCP)

Gumawa at mag-edit ng widgets na ito diretso mula sa Claude Desktop o Claude Code. Mag-generate ng token, i-add ang connector, at kayang tawagin ni Claude ang tools na ito sa account mo:

get_docsAng buong authoring guide + bawat trigger at action (ito ang unang binabasa ni Claude).
list_widgetsI-list ang saved widgets mo kasama ang ids nila.
get_widgetKunin ang html / css / js ng isang widget.
create_widgetGumawa ng bagong HTML widget mula sa name + html/css/js.
update_widgetI-edit ang isang existing na widget.
delete_widgetI-delete ang widget gamit ang id.
list_templatesI-list ang built-in templates na puwedeng kopyahin.
get_templateBasahin ang html/css/js ng isang template.
create_from_templateKopyahin ang template sa isang bagong widget.
list_community_templatesI-browse ang widgets na na-publish ng community.
install_community_templateMag-install ng private at editable na fork ng community template.
Naglo-load…
03

Ang TE SDK

Lahat ng ginagawa ng widget ay dumadaan sa global na window.TE object.

TE.on(type, fn)

Mag-subscribe sa isang live event. Ang `type` ay kahit anong trigger sa ibaba; tumatakbo ang `fn(ev)` tuwing nangyayari ito.

TE.on('*', fn)

Saluhin ang bawat event. Natatanggap ng `fn(ev, type)` ang payload at ang event name.

TE.off(type, fn)

Tanggalin ang handler na na-register dati.

TE.onGift(name, fn)

Mag-fire lang para sa gifts na tugma ang pangalan (case-insensitive substring). Alisin ang `name` para saluhin ang bawat gift.

TE.onSticker(idOrUrl, fn)

Mag-fire lang para sa isang specific na subscriber emote / sticker — tinutugma gamit ang TikTok id, image URL, o source nito.

TE.rules.allowUser / allowGift

Reusable eligibility filters para sa roles, allow/deny lists, gift name at minimum coin value.

TE.metrics

Ang pinakabagong stream snapshot object (viewers, likes, coins, followers, topGifters…). Dumarating din via TE.on('metrics', fn).

TE.demo

True kung saan ipinapakita ang widget (gallery, preview tiles, ang Demo switch ng builder). False sa live overlay at habang nagte-test ka sa builder o overlay editor, kung saan ang fine-fire mo lang o ang dumarating live ang lumalabas. Ipinapakita ng bawat widget ang sarili nito doon: ilagay ang samples na mukhang totoong events (totoong gift artwork, names, avatars) sa isang if (TE.demo) block, gamit ang totoong code path ng widget. Ang widget na may ganitong block ay hindi makakakuha ng preview events mula sa host, kaya walang nagfa-fire nang dalawang beses.

TE.defineSettings([...])

Mag-declare ng controls na puwedeng i-tweak ng streamer; ibinabalik ang current values (defaults na naka-merge sa mga pinili ng streamer).

TE.settings

Ang current settings values object (parehong shape na ibinalik ng defineSettings).

TE.on('settings', fn)

Tumatakbo kapag binago ng streamer ang isang setting live — mag-re-render gamit ang bagong values.

TE.state.get/set/increment

Promise-based atomic values para sa totals at shared game state.

TE.collection.*

Server-owned na join-once participants at score tables: join, increment, list, count, remove at clear.

TE.queue.*

Bounded server-owned FIFO queues para sa media, requests at viewer-triggered actions.

TE.shared.*

Isama ang ilang widgets sa iisang named channel-state namespace; nananatiling isolated ang ibang widgets.

TE.random.draw(name, opts)

Securely bumunot ng isa o higit pang winners mula sa collection at i-record ang resulta.

TE.cooldown.claim(scope, user, ms)

Atomically mag-claim ng global o per-user cooldown; ibinabalik ang claimed at retryAfterMs.

TE.timer.*

I-start, i-pause, i-reset at basahin ang persisted wall-clock timer na hindi nawawala sa reload.

TE.points.trySpend(user, amount, reason)

I-check at i-debit ang points sa iisang transaction; laging tingnan ang result.ok bago magpatakbo ng paid interaction.

04

Triggers

Live events na puwede mong pakinggan. Bawat user ay may { id, name, username, avatar, roles }.

TE.on('gift')

Nag-send ng gift ang isang viewer. coins = total para sa combo; minamarkahan ng streakEnd na tapos na ang combo.

{ user, gift: { name, image|null, coins, repeat, combo, streakEnd } }
TE.on('follow')

Nag-follow ang isang viewer sa account.

{ user }
TE.on('subscribe')

Nag-subscribe ang isang viewer. months = ilang buwan nang sunod-sunod.

{ user, months }
TE.on('share')

Ni-share ng isang viewer ang live.

{ user }
TE.on('chat')

Isang chat message. emotes = inline subscriber-emote image URLs sa message na ito.

{ user, comment, emotes:[url,…] }
TE.on('like')

Nag-send ng likes ang isang viewer. count = ang burst na ito; total = running total.

{ user, count, total }
TE.on('join')

Pumasok sa room ang isang viewer. isTop = pumasok ang isang top gifter.

{ user, isTop }
TE.on('sticker')

Subscriber emote o on-screen sticker na na-send live. url = ang emote image; id = ang TikTok id nito.

{ user|null, id|null, url, source }
TE.on('metrics')

Ang pinakabagong stream snapshot. Hawak ito ng TE.metrics; nagfa-fire ang event tuwing nag-a-update ito. Ang hostNick at hostAvatar ay ang sariling pangalan at profile picture ng streamer.

{ live, viewers, likes, followers, coins, hostNick, hostAvatar, topGifters:[…], … }
TE.on('milestone')

Nalampasan ang isang round-number milestone (likes, coins, followers, subs).

{ metric, value, label }
TE.on('poll')

Ang native live poll ng TikTok — totoong votes, mula simula hanggang dulo.

{ state:'start'|'update'|'end', title, options:[{text,votes}], endsAt }
TE.on('battle')

LinkMic battle updates: battle cards, fan tickets, army sizes.

{ card|null, tickets|null, armies|null, battleId|null }
TE.on('rank')

Posisyon sa hourly ranking at mga rank-up moment.

{ rank|null, from|null, to|null, countdown|null }
TE.on('envelope')

May bumagsak na red envelope / treasure box sa live.

{ }
TE.on('pinned')

Nag-pin ng comment ang host.

{ text }
TE.on('deleted')

May tinanggal na chat message ang isang moderator (tugma ang id sa naunang chat event).

{ id }
TE.on('streamState')

Nag-live o nag-offline ang stream.

{ live }
TE.on('apiEvent')

Custom event na ipinost gamit ang Event API. Makinig sa apiEvent o diretso sa custom name nito.

{ name, data }
TE.on('nowPlaying')

Ang current Spotify track ng streamer KASAMA ang up-next queue. Nagfa-fire pag nag-connect at tuwing nagbabago ang kanta, play-state, progress o queue. null ang playback kapag walang tumutugtog; ang queue ay ang mga susunod na tracks.

{ connected, playback: { isPlaying, progressMs, durationMs, title, artists:[…], album, artwork|null } | null, queue:[{ title, artist, artwork|null, durationMs }] }
Isang specific na gift o emote lang
TE.onGift('Rose', function (ev) { /* … */ });
TE.onSticker('<sticker id>', function (ev) { /* … */ });

Nasa widget builder ang emotes ng channel mo: buksan ang Simulate events, piliin ang Sticker at pumili ng isa. Dala ng test event ang totoong id at image URL nito, kaya ang handler na naka-key sa alinman ay na-te-test gamit ang totoong emote.

05

Actions

Ano ang kayang gawin ng widget mo bilang sagot — plain browser APIs, ready nang i-paste.

Magpakita ng image

Magpalabas ng image sa screen — ang gift/emote artwork, isang na-upload na asset, o kahit anong URL. Kusang nawawala pagkalipas ng ilang segundo.

// Show an image, then fade it out
TE.on('gift', function (ev) {
  var img = document.createElement('img');
  img.src = ev.gift.image;                 // ← any image URL works
  img.style.cssText = 'position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);max-width:60%';
  document.body.appendChild(img);
  setTimeout(function () { img.remove(); }, 4000);   // ← how long it stays
});
Mag-play ng sound

Mag-play ng audio sa isang event. Maglagay ng sound setting para ang streamer ang pumili ng sariling file — walang code editing.

// Play a sound the streamer chose in settings
var s = TE.defineSettings([{ key: 'sfx', label: 'Alert sound', type: 'sound', default: '' }]);
TE.on('settings', function (ns) { s = ns; });
TE.on('gift', function (ev) {
  if (s.sfx) { var a = new Audio(s.sfx); a.volume = 0.8; a.play(); }
});
Magpakita / mag-animate ng text

Magsulat ng dynamic text at i-re-trigger ang CSS animation sa pag-toggle ng class.

// Announce the event with a CSS pop animation
var box = document.getElementById('box');   // your element in the HTML tab
TE.on('follow', function (ev) {
  box.textContent = ev.user.name + ' followed!';
  box.classList.remove('pop'); void box.offsetWidth; box.classList.add('pop');
});
Magpakita ng live stat

I-bind ang numero sa screen sa isang live metric — viewers, likes, coins, followers — automatic na nag-a-update.

// Keep a number in sync with the live stream
TE.on('metrics', function (m) {
  document.getElementById('count').textContent = m.viewers.toLocaleString();
});
Basahin nang malakas (TTS)

Sabihin ang event gamit ang text-to-speech ng browser. Swak para sa gift o follow shout-outs.

// Text-to-speech shout-out
TE.on('gift', function (ev) {
  var u = new SpeechSynthesisUtterance(ev.user.name + ' sent ' + ev.gift.name);
  speechSynthesis.speak(u);
});
Preview (TE.demo)

Ipinapakita ng bawat widget ang sarili nito sa gallery at sa builder: samples na mukhang totoong events, may totoong gift artwork, names at avatars. Sa live, false ang TE.demo at totoong events lang ang lumalabas.

// Preview samples through the real code path. Never on a live overlay.
if (TE.demo) {
  var GIFTS = [
    { name: 'Rose', coins: 1, image: 'https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/eba3a9bb85c33e017f3648eaf88d7189~tplv-obj.png' },
    { name: 'Perfume', coins: 20, image: 'https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/20b8f61246c7b6032777bb81bf4ee055~tplv-obj.png' }
  ];
  var NAMES = ['lunaa', 'nightowl_gaming_official'], n = 0;
  function sample() {
    n++;
    onGift({ user: { name: NAMES[n % 2], avatar: window.__TE_AVATAR }, gift: GIFTS[n % 2] });
  }
  sample();
  setInterval(sample, 6000);
}
Tandaan ang mga bagay (TE.store)

Simpleng synchronous persistence para sa visual state. Gamitin ang transactional runtime para sa entrants, purchases at shared scores.

// Persistent state: survives OBS reloads
var total = TE.store.get('total', 0);          // read (with default)
TE.on('gift', function (ev) {
  total += ev.gift.coins;
  TE.store.set('total', total);                 // write (auto-saved)
  render();
});
Mag-react sa chat commands

Exact-match chat commands kasama ang atomic collections para maging safe ang entries, votes at spins.

// One server-authoritative entry per viewer.
// A function keyword reads s.command on every message, so the
// streamer can change the command in settings without a reload.
TE.onCommand(function () { return s.command || '!join'; }, function (c) {
  TE.collection.join('entrants', c.user.id, c.user).then(function (result) {
    if (result.joined) renderCount(result.count);
  });
});
Mag-trigger ng stream actions (TE.act)

Mag-request ng overlay alerts, sounds, TTS, counters, subathon time o OBS scene switches diretso mula sa isang widget.

// One call, one stream action
TE.act({ id: 'tts', message: 'New high score!' });
TE.act({ id: 'points', user: ev.user, amount: 50 });
Magbigay ng viewer points

Transactionally i-check, gastusin o ibigay ang loyalty points at matanggap ang resulting balance.

// Only spin after the points were really debited
TE.onCommand(function () { return s.command || '!spin'; }, function (ev) {
  TE.points.trySpend(ev.user, 50, 'Wheel spin').then(function (result) {
    if (result.ok) spin();
    else showMissing(result.missing);
  });
});
Gumawa ng fair games

Atomic state, collections, secure random draws, cooldowns at persistent timers — safe kahit may duplicate browser sources.

// Fair draw from a server-owned entrant collection
TE.random.draw('entrants', { count: 1 }).then(function (result) {
  if (result.ok) reveal(result.winners[0].value);
});
Magdagdag ng streamer controls

Maglagay ng text, colors, numbers, toggles — tine-tweak ito ng streamer live sa overlay editor, walang code na kailangan.

// No-code controls for whoever uses the widget
var s = TE.defineSettings([
  { key: 'title',  label: 'Title', type: 'text',  default: 'Goal' },
  { key: 'accent', label: 'Color', type: 'color', default: '#FF2E4D' },
]);
document.body.style.setProperty('--accent', s.accent);
TE.on('settings', function (ns) { s = ns; /* re-render with new values */ });
06

Streamer settings

Mag-declare ng controls gamit ang TE.defineSettings([...]) at ang kahit sinong gagamit ng widget ay puwedeng mag-tweak nito live sa overlay editor — walang code. Bawat field:

Ang naka-place na widget na may isa o higit pang 'button' fields ay nagpapakita ng actions na iyon sa settings nito sa loob ng Overlay Editor. Ipinapakita rin ng Widget Builder ang parehong buttons para sa safe na preview testing habang nagko-code.

uriipinapakitavalue
'text'Single-line text inputstring
'number'Number inputnumber
'color'Color pickerhex string, hal. "#FF2E4D"
'select'Dropdown (kailangan ng options: [...])isa sa options
'toggle'On/off switchboolean
'range'Slider (min / max / step)number
'button'Declarative action buttonincrement, set o toggle — validated JSON, hindi kailanman dashboard code
'sound'Picker ng na-upload na sounds ng streamer + Uploadang napiling file URL ("" = wala)
'image'Picker ng na-upload na images ng streamer + Uploadang napiling file URL ("" = wala)
var s = TE.defineSettings([
  { key: 'title',  label: 'Title', type: 'text',   default: 'Follower goal' },
  { key: 'target', label: 'Goal',  type: 'number', default: 100 },
  { key: 'accent', label: 'Color', type: 'color',  default: '#FF2E4D' },
  { key: 'reset',  label: 'Reset', type: 'button', default: 0,
    action: { type: 'increment', step: 1 }, confirm: true, tone: 'danger' },
]);
// re-render when the streamer changes something live
var lastReset = s.reset;
TE.on('settings', function (ns) {
  s = ns;
  if (ns.reset !== lastReset) { lastReset = ns.reset; resetCounter(); }
  render();
});

Validated data ang control definitions. Ang button action ay tumatanggap lang ng increment, set o toggle. Ang HTML, callback code at arbitrary URLs ay nire-reject o ini-ignore ng dashboard renderer.

07

Sandbox at limits

Tumatakbo ang widgets sa isolated iframe na may strict content-security policy, kaya ang sira o malisyosong widget ay hindi kailanman makakagalaw sa account mo o sa ibang bahagi ng page.

Allowed
  • Remote images at GIFs (TikTok gift art, avatars, kahit anong URL)
  • Google Fonts + sarili mong @font-face
  • Audio via new Audio(url)
  • Requests sa sariling APIs ng TokElements (hal. /api/files/…)
  • CSS animations, SVG, canvas, Web Audio
Blocked
  • External <script> tags / CDNs
  • fetch / XHR / WebSocket papunta sa ibang hosts
  • Cookies, localStorage, access sa parent page
  • Pag-load ng npm packages
08

Examples

Kumpleto at copy-paste na widgets. Live ang bawat preview — nagfa-fire ang demo events para mapanood mo itong mag-react.

Emote reaction· pinapalabas ang emote kapag na-send ito sa chat
var img = document.getElementById('emote');
// fires for every emote — use TE.onSticker('<id>', …) for one specific one
TE.on('sticker', function (ev) {
  img.src = ev.url;
  img.classList.remove('show'); void img.offsetWidth; img.classList.add('show');
});
Live preview
Follower goal bar· bina-bind ang bar sa isang live metric
var target = 200;                       // ← your goal
TE.on('metrics', function (m) {
  var pct = Math.min(100, m.follows / target * 100);
  document.getElementById('fill').style.width = pct + '%';
  document.getElementById('txt').textContent = m.follows + ' / ' + target;
});
Live preview