Pierwsze kroki
Zakładki HTML i CSS rysują Twój widget. Ramka jest przezroczysta i unosi się nad obrazem – nigdy nie maluj pełnego, nieprzezroczystego tła.
W zakładce JS subskrybuj przez TE.on('gift', fn). window.TE jest już załadowane – bez importów, bez konfiguracji.
Użyj Symuluj albo przycisków Fire, żeby podejrzeć offline, a potem Dodaj do overlayu – albo pozwól Claude zbudować go przez MCP.
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');
});Połącz Claude (MCP)
Twórz i edytuj te widgety prosto z Claude Desktop lub Claude Code. Wygeneruj token, dodaj konektor, a Claude będzie mógł wywoływać te narzędzia na Twoim koncie:
get_docsPełny poradnik tworzenia + każdy trigger i akcja (Claude czyta to najpierw).list_widgetsLista zapisanych widgetów z ich id.get_widgetPobiera html / css / js jednego widgetu.create_widgetTworzy nowy widget HTML z nazwy + html/css/js.update_widgetEdytuje istniejący widget.delete_widgetUsuwa widget po id.list_templatesLista wbudowanych szablonów do skopiowania.get_templateOdczytuje html/css/js szablonu.create_from_templateKopiuje szablon do nowego widgetu.list_community_templatesPrzeglądaj widgety opublikowane przez społeczność.install_community_templateInstaluje prywatną, edytowalną kopię szablonu od społeczności.SDK TE
Wszystko, co robi widget, przechodzi przez globalny obiekt window.TE.
TE.on(type, fn)Subskrybuje zdarzenie na żywo. `type` to dowolny trigger poniżej; `fn(ev)` uruchamia się za każdym razem, gdy ono wystąpi.
TE.on('*', fn)Łapie każde zdarzenie. `fn(ev, type)` dostaje dane i nazwę zdarzenia.
TE.off(type, fn)Usuwa wcześniej zarejestrowany handler.
TE.onGift(name, fn)Odpala się tylko dla prezentów o pasującej nazwie (fragment tekstu, bez rozróżniania wielkości liter). Pomiń `name`, żeby łapać każdy prezent.
TE.onSticker(idOrUrl, fn)Odpala się tylko dla jednej konkretnej emotki / naklejki subskrybenta – dopasowanej po id TikToka, URL obrazka albo źródle.
TE.rules.allowUser / allowGiftFiltry uprawnień wielokrotnego użytku dla ról, list dozwolonych/zabronionych, nazwy prezentu i minimalnej wartości w monetach.
TE.metricsNajnowszy obiekt migawki streamu (viewers, likes, coins, followers, topGifters…). Dostarczany też przez TE.on('metrics', fn).
TE.demoTrue tam, gdzie widget jest prezentowany (galeria, kafelki podglądu, przełącznik Demo w kreatorze). False na overlayu na żywo oraz podczas testów w kreatorze lub edytorze overlayu, gdzie pojawia się tylko to, co odpalisz, albo to, co przychodzi na żywo. Każdy widget pokazuje się tam sam: przykłady wyglądające jak prawdziwe zdarzenia (prawdziwe grafiki prezentów, nicki, awatary) umieść w bloku if (TE.demo), przez prawdziwą ścieżkę kodu widgetu. Widget z takim blokiem nie dostaje od hosta zdarzeń podglądu, więc nic nie odpala się dwa razy.
TE.defineSettings([...])Deklaruje kontrolki, które streamer może zmieniać; zwraca aktualne wartości (domyślne połączone z wyborami streamera).
TE.settingsAktualny obiekt z wartościami ustawień (ten sam kształt, który zwróciło defineSettings).
TE.on('settings', fn)Uruchamia się, gdy streamer zmienia ustawienie na żywo – wyrenderuj ponownie z nowymi wartościami.
TE.state.get/set/incrementAtomowe wartości oparte na Promise do sum i wspólnego stanu gry.
TE.collection.*Zarządzani przez serwer uczestnicy (dołączenie raz) i tabele wyników: join, increment, list, count, remove i clear.
TE.queue.*Ograniczone kolejki FIFO po stronie serwera na media, prośby i akcje wywoływane przez widzów.
TE.shared.*Łączy kilka widgetów w jedną nazwaną przestrzeń wspólnego stanu kanału; niepowiązane widgety pozostają odizolowane.
TE.random.draw(name, opts)Bezpiecznie losuje jednego lub więcej zwycięzców z kolekcji i zapisuje wynik.
TE.cooldown.claim(scope, user, ms)Atomowo zajmuje globalny lub per-użytkownik cooldown; zwraca claimed i retryAfterMs.
TE.timer.*Uruchamia, wstrzymuje, resetuje i odczytuje zapisany timer czasu rzeczywistego, który przetrwa przeładowanie.
TE.points.trySpend(user, amount, reason)Sprawdza i pobiera punkty w jednej transakcji; zawsze sprawdź result.ok przed uruchomieniem płatnej interakcji.
Triggery
Zdarzenia na żywo, których możesz nasłuchiwać. Każdy user zawiera { id, name, username, avatar, roles }.
TE.on('gift')Widz wysłał prezent. coins = suma za combo; streakEnd oznacza koniec combo.
{ user, gift: { name, image|null, coins, repeat, combo, streakEnd } }TE.on('follow')Widz zaobserwował konto.
{ user }TE.on('subscribe')Widz wykupił subskrypcję. months = ile miesięcy z rzędu.
{ user, months }TE.on('share')Widz udostępnił LIVE.
{ user }TE.on('chat')Wiadomość na czacie. emotes = URL-e obrazków emotek subskrybentów w tej wiadomości.
{ user, comment, emotes:[url,…] }TE.on('like')Widz wysłał lajki. count = ta seria; total = suma bieżąca.
{ user, count, total }TE.on('join')Widz wszedł do pokoju. isTop = dołączył top darczyńca.
{ user, isTop }TE.on('sticker')Emotka subskrybenta lub naklejka na ekranie wysłana na żywo. url = obrazek emotki; id = jej id na TikToku.
{ user|null, id|null, url, source }TE.on('metrics')Najnowsza migawka streamu. TE.metrics ją przechowuje; zdarzenie odpala się przy każdej aktualizacji. hostNick i hostAvatar to nick i zdjęcie profilowe samego streamera.
{ live, viewers, likes, followers, coins, hostNick, hostAvatar, topGifters:[…], … }TE.on('milestone')Przekroczono okrągły kamień milowy (lajki, monety, obserwujący, suby).
{ metric, value, label }TE.on('poll')Natywna ankieta TikTok LIVE – prawdziwe głosy, od początku do końca.
{ state:'start'|'update'|'end', title, options:[{text,votes}], endsAt }TE.on('battle')Aktualizacje bitew LinkMic: karty bitwy, fan tickets, wielkość armii.
{ card|null, tickets|null, armies|null, battleId|null }TE.on('rank')Pozycja w rankingu godzinowym i momenty awansu.
{ rank|null, from|null, to|null, countdown|null }TE.on('envelope')Czerwona koperta / skrzynia skarbów zrzucona na LIVE.
{ }TE.on('pinned')Host przypiął komentarz.
{ text }TE.on('deleted')Moderator usunął wiadomość z czatu (id pasuje do wcześniejszego zdarzenia chat).
{ id }TE.on('streamState')Stream wystartował lub się zakończył.
{ live }TE.on('apiEvent')Własne zdarzenie wysłane przez Event API. Nasłuchuj apiEvent albo bezpośrednio jego własnej nazwy.
{ name, data }TE.on('nowPlaying')Aktualny utwór streamera na Spotify ORAZ kolejka następnych. Odpala się po połączeniu i przy każdej zmianie utworu, stanu odtwarzania, postępu lub kolejki. playback to null, gdy nic nie gra; queue to nadchodzące utwory.
{ connected, playback: { isPlaying, progressMs, durationMs, title, artists:[…], album, artwork|null } | null, queue:[{ title, artist, artwork|null, durationMs }] }TE.onGift('Rose', function (ev) { /* … */ });
TE.onSticker('<sticker id>', function (ev) { /* … */ });Emotki Twojego kanału są w kreatorze widgetów: otwórz Symuluj zdarzenia, wybierz Naklejka i wskaż jedną. Zdarzenie testowe ma jej prawdziwe id i URL obrazka, więc handler oparty na którymkolwiek z nich jest testowany na prawdziwym przykładzie.
Akcje
Co Twój widget może zrobić w odpowiedzi – zwykłe API przeglądarki, gotowe do wklejenia.
Wyświetl obrazek na ekranie – grafikę prezentu/emotki, wgrany plik albo dowolny URL. Znika sam po kilku sekundach.
// 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
});Odtwórz dźwięk przy zdarzeniu. Udostępnij ustawienie dźwięku, żeby streamer wybrał własny plik – bez edycji kodu.
// 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(); }
});Wypisz dynamiczny tekst i ponownie odpal animację CSS, przełączając klasę.
// 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');
});Powiąż liczbę na ekranie z metryką na żywo – widzowie, lajki, monety, obserwujący – aktualizowaną automatycznie.
// Keep a number in sync with the live stream
TE.on('metrics', function (m) {
document.getElementById('count').textContent = m.viewers.toLocaleString();
});Wypowiedz zdarzenie przez syntezator mowy przeglądarki. Świetne do podziękowań za prezenty i obserwacje.
// Text-to-speech shout-out
TE.on('gift', function (ev) {
var u = new SpeechSynthesisUtterance(ev.user.name + ' sent ' + ev.gift.name);
speechSynthesis.speak(u);
});Każdy widget pokazuje się w galerii i kreatorze: przykłady wyglądające jak prawdziwe zdarzenia, z prawdziwymi grafikami prezentów, nickami i awatarami. Na żywo TE.demo ma wartość false i pojawiają się tylko prawdziwe zdarzenia.
// 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);
}Prosta synchroniczna trwałość dla stanu wizualnego. Do uczestników, zakupów i wspólnych wyników używaj transakcyjnego runtime’u.
// 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();
});Dokładnie dopasowane komendy z czatu plus atomowe kolekcje umożliwiają bezpieczne zapisy, głosowania i losowania.
// 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);
});
});Wywołuj alerty overlayu, dźwięki, TTS, liczniki, czas subathonu albo przełączanie scen OBS bezpośrednio z widgetu.
// One call, one stream action
TE.act({ id: 'tts', message: 'New high score!' });
TE.act({ id: 'points', user: ev.user, amount: 50 });Transakcyjnie sprawdzaj, wydawaj lub przyznawaj punkty lojalnościowe i otrzymuj wynikowe saldo.
// 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);
});
});Atomowy stan, kolekcje, bezpieczne losowania, cooldowny i trwałe timery – bezpieczne przy zduplikowanych źródłach przeglądarki.
// 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);
});Udostępnij tekst, kolory, liczby, przełączniki – streamer zmienia je na żywo w edytorze overlayu, bez kodu.
// 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 */ });Ustawienia streamera
Zadeklaruj kontrolki przez TE.defineSettings([...]), a każdy, kto używa widgetu, zmienia je na żywo w edytorze overlayu – bez kodu. Każde pole:
Umieszczony widget z jednym lub kilkoma polami 'button' udostępnia te akcje w swoich ustawieniach w Edytorze overlayu. Kreator widgetów pokazuje te same przyciski do bezpiecznego testowania podczas kodowania.
'text'Jednoliniowe pole tekstowestring'number'Pole liczbowenumber'color'Próbnik kolorustring hex, np. "#FF2E4D"'select'Lista rozwijana (wymaga options: [...])jedna z opcji'toggle'Przełącznik wł./wył.boolean'range'Suwak (min / max / step)number'button'Deklaratywny przycisk akcjiincrement, set lub toggle – walidowany JSON, nigdy kod panelu'sound'Wybór spośród wgranych dźwięków streamera + UploadURL wybranego pliku ("" = brak)'image'Wybór spośród wgranych obrazków streamera + UploadURL wybranego pliku ("" = brak)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();
});Definicje kontrolek to walidowane dane. Przycisk action przyjmuje tylko increment, set lub toggle. HTML, kod callbacków i dowolne URL-e są odrzucane lub ignorowane przez renderer panelu.
Piaskownica i limity
Widgety działają w odizolowanym iframe ze ścisłą polityką bezpieczeństwa treści (CSP), więc zepsuty lub złośliwy widget nigdy nie dotknie Twojego konta ani reszty strony.
- Zdalne obrazki i GIF-y (grafiki prezentów TikTok, awatary, dowolny URL)
- Google Fonts + własne @font-face
- Dźwięk przez new Audio(url)
- Zapytania do własnych API TokElements (np. /api/files/…)
- Animacje CSS, SVG, canvas, Web Audio
- Zewnętrzne tagi <script> / CDN-y
- fetch / XHR / WebSocket do innych hostów
- Ciasteczka, localStorage, dostęp do strony nadrzędnej
- Ładowanie pakietów npm
Przykłady
Kompletne widgety do skopiowania i wklejenia. Każdy podgląd działa na żywo – zdarzenia demo się odpalają, więc widzisz, jak reaguje.
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');
});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;
});