Erste Schritte
Die Tabs HTML & CSS zeichnen dein Widget. Die Box ist transparent und schwebt über dem Video – mal nie einen vollflächigen, deckenden Hintergrund.
Im JS-Tab abonnierst du mit TE.on('gift', fn). window.TE ist schon geladen – keine Imports, kein Setup.
Nutze Simulieren oder die Auslösen-Buttons für eine Offline-Vorschau, dann Zum Overlay hinzufügen – oder lass Claude es über MCP bauen.
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');
});Claude verbinden (MCP)
Baue und bearbeite diese Widgets direkt aus Claude Desktop oder Claude Code. Erzeuge ein Token, füge den Connector hinzu, und Claude kann diese Tools in deinem Konto aufrufen:
get_docsDer komplette Authoring-Guide + jeder Trigger und jede Aktion (Claude liest das zuerst).list_widgetsListet deine gespeicherten Widgets mit ihren IDs.get_widgetHolt HTML / CSS / JS eines Widgets.create_widgetErstellt ein neues HTML-Widget aus Name + HTML/CSS/JS.update_widgetBearbeitet ein bestehendes Widget.delete_widgetLöscht ein Widget per ID.list_templatesListet die eingebauten Vorlagen zum Kopieren.get_templateLiest HTML/CSS/JS einer Vorlage.create_from_templateKopiert eine Vorlage in ein neues Widget.list_community_templatesDurchstöbert von der Community veröffentlichte Widgets.install_community_templateInstalliert einen privaten, bearbeitbaren Fork einer Community-Vorlage.Das TE-SDK
Alles, was ein Widget tut, läuft über das globale window.TE-Objekt.
TE.on(type, fn)Abonniert ein Live-Event. `type` ist einer der Trigger unten; `fn(ev)` läuft jedes Mal, wenn er eintritt.
TE.on('*', fn)Fängt jedes Event ab. `fn(ev, type)` bekommt die Nutzdaten und den Event-Namen.
TE.off(type, fn)Entfernt einen zuvor registrierten Handler.
TE.onGift(name, fn)Löst nur bei Geschenken aus, deren Name passt (Teilstring, Groß-/Kleinschreibung egal). Lass `name` weg, um jedes Geschenk abzufangen.
TE.onSticker(idOrUrl, fn)Löst nur bei einem bestimmten Abonnenten-Emote / Sticker aus – erkannt an seiner TikTok-ID, Bild-URL oder Quelle.
TE.rules.allowUser / allowGiftWiederverwendbare Berechtigungsfilter für Rollen, Allow-/Deny-Listen, Geschenkname und Mindestwert in Münzen.
TE.metricsDas aktuelle Stream-Snapshot-Objekt (viewers, likes, coins, followers, topGifters…). Kommt auch über TE.on('metrics', fn).
TE.demoTrue dort, wo das Widget vorgeführt wird (Galerie, Vorschaukacheln, der Demo-Schalter im Builder). False auf einem Live-Overlay und beim Testen im Builder oder Overlay-Editor, wo nur erscheint, was du auslöst oder was live hereinkommt. Jedes Widget zeigt sich dort selbst: Leg Beispiele, die wie echte Events aussehen (echtes Geschenk-Artwork, Namen, Avatare), in einen if (TE.demo)-Block, über den echten Code-Pfad des Widgets. Ein Widget mit so einem Block bekommt vom Host keine Vorschau-Events, damit nichts doppelt auslöst.
TE.defineSettings([...])Deklariert Regler, die der Streamer anpassen kann; gibt die aktuellen Werte zurück (Standardwerte zusammengeführt mit der Auswahl des Streamers).
TE.settingsDas aktuelle Objekt mit den Einstellungswerten (dieselbe Form, die defineSettings zurückgegeben hat).
TE.on('settings', fn)Läuft, wenn der Streamer live eine Einstellung ändert – rendere mit den neuen Werten neu.
TE.state.get/set/incrementPromise-basierte atomare Werte für Summen und gemeinsamen Spielstand.
TE.collection.*Serverseitige Teilnehmer (einmal beitreten) und Punktetabellen: join, increment, list, count, remove und clear.
TE.queue.*Begrenzte serverseitige FIFO-Warteschlangen für Medien, Wünsche und von Zuschauern ausgelöste Aktionen.
TE.shared.*Bindet mehrere Widgets an einen benannten gemeinsamen Kanal-Zustand; andere Widgets bleiben isoliert.
TE.random.draw(name, opts)Zieht sicher einen oder mehrere Gewinner aus einer Sammlung und speichert das Ergebnis.
TE.cooldown.claim(scope, user, ms)Beansprucht atomar einen globalen oder nutzerbezogenen Cooldown; gibt claimed und retryAfterMs zurück.
TE.timer.*Startet, pausiert, setzt zurück und liest einen gespeicherten Echtzeit-Timer, der Neuladen übersteht.
TE.points.trySpend(user, amount, reason)Prüft und bucht Punkte in einer Transaktion ab; prüf immer result.ok, bevor du eine kostenpflichtige Interaktion ausführst.
Trigger
Live-Events, auf die du hören kannst. Jeder user enthält { id, name, username, avatar, roles }.
TE.on('gift')Ein Zuschauer hat ein Geschenk geschickt. coins = Summe der Combo; streakEnd markiert das Ende der Combo.
{ user, gift: { name, image|null, coins, repeat, combo, streakEnd } }TE.on('follow')Ein Zuschauer folgt dem Account.
{ user }TE.on('subscribe')Ein Zuschauer hat abonniert. months = wie viele Monate am Stück.
{ user, months }TE.on('share')Ein Zuschauer hat das LIVE geteilt.
{ user }TE.on('chat')Eine Chatnachricht. emotes = URLs der Abonnenten-Emote-Bilder in dieser Nachricht.
{ user, comment, emotes:[url,…] }TE.on('like')Ein Zuschauer hat Likes geschickt. count = dieser Schwung; total = laufende Summe.
{ user, count, total }TE.on('join')Ein Zuschauer hat den Raum betreten. isTop = ein Top-Gifter ist beigetreten.
{ user, isTop }TE.on('sticker')Ein Abonnenten-Emote oder Bildschirm-Sticker wurde live gesendet. url = das Emote-Bild; id = seine TikTok-ID.
{ user|null, id|null, url, source }TE.on('metrics')Der aktuelle Stream-Snapshot. TE.metrics enthält ihn; das Event feuert bei jeder Aktualisierung. hostNick und hostAvatar sind Name und Profilbild des Streamers.
{ live, viewers, likes, followers, coins, hostNick, hostAvatar, topGifters:[…], … }TE.on('milestone')Ein runder Meilenstein wurde überschritten (Likes, Münzen, Follower, Abos).
{ metric, value, label }TE.on('poll')TikToks eigene Live-Umfrage – echte Stimmen, vom Start bis zum Ende.
{ state:'start'|'update'|'end', title, options:[{text,votes}], endsAt }TE.on('battle')Updates zu LinkMic-Battles: Battle-Karten, Fan-Tickets, Armeegrößen.
{ card|null, tickets|null, armies|null, battleId|null }TE.on('rank')Position im Stundenranking und Momente des Aufstiegs.
{ rank|null, from|null, to|null, countdown|null }TE.on('envelope')Ein roter Umschlag / eine Schatztruhe wurde im LIVE abgeworfen.
{ }TE.on('pinned')Der Host hat einen Kommentar angepinnt.
{ text }TE.on('deleted')Ein Moderator hat eine Chatnachricht entfernt (id entspricht dem früheren Chat-Event).
{ id }TE.on('streamState')Der Stream ist live gegangen oder offline.
{ live }TE.on('apiEvent')Ein eigenes Event, gesendet über die Event-API. Hör auf apiEvent oder direkt auf seinen eigenen Namen.
{ name, data }TE.on('nowPlaying')Der aktuelle Spotify-Track des Streamers PLUS die kommende Warteschlange. Feuert beim Verbinden und immer, wenn sich Song, Wiedergabestatus, Fortschritt oder Warteschlange ändern. playback ist null, wenn nichts läuft; queue sind die kommenden Tracks.
{ 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) { /* … */ });Die Emotes deines Kanals findest du im Widget-Builder: Öffne Events simulieren, wähle Sticker und such dir eins aus. Das Test-Event enthält die echte ID und Bild-URL, sodass ein Handler, der auf eins davon hört, mit dem echten Emote getestet wird.
Aktionen
Was dein Widget als Reaktion tun kann – einfache Browser-APIs, bereit zum Einfügen.
Lass ein Bild aufploppen – das Geschenk-/Emote-Artwork, ein hochgeladenes Asset oder eine beliebige URL. Verschwindet nach ein paar Sekunden von selbst.
// 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
});Spiel bei einem Event Audio ab. Biete eine Sound-Einstellung an, damit der Streamer seine eigene Datei wählt – ohne Code zu bearbeiten.
// 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(); }
});Schreib dynamischen Text und starte eine CSS-Animation neu, indem du eine Klasse umschaltest.
// 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');
});Verknüpfe eine Zahl auf dem Bildschirm mit einer Live-Metrik – Zuschauer, Likes, Münzen, Follower –, automatisch aktualisiert.
// Keep a number in sync with the live stream
TE.on('metrics', function (m) {
document.getElementById('count').textContent = m.viewers.toLocaleString();
});Lies ein Event mit der Text-to-Speech-Funktion des Browsers vor. Ideal für Geschenk- oder Follow-Shoutouts.
// Text-to-speech shout-out
TE.on('gift', function (ev) {
var u = new SpeechSynthesisUtterance(ev.user.name + ' sent ' + ev.gift.name);
speechSynthesis.speak(u);
});Jedes Widget zeigt sich in der Galerie und im Builder selbst: Beispiele, die wie echte Events aussehen, mit echtem Geschenk-Artwork, Namen und Avataren. Live ist TE.demo false und nur echte Events erscheinen.
// 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);
}Einfache synchrone Speicherung für visuellen Zustand. Nutze die transaktionale Runtime für Teilnehmer, Käufe und gemeinsame Punktestände.
// 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();
});Exakt passende Chat-Befehle plus atomare Sammlungen ermöglichen sichere Teilnahmen, Abstimmungen und Drehs.
// 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);
});
});Fordere Overlay-Alerts, Sounds, TTS, Zähler, Subathon-Zeit oder OBS-Szenenwechsel direkt aus einem Widget an.
// One call, one stream action
TE.act({ id: 'tts', message: 'New high score!' });
TE.act({ id: 'points', user: ev.user, amount: 50 });Prüfe, gib aus oder vergib Treuepunkte transaktional und erhalte den neuen Kontostand.
// 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);
});
});Atomarer Zustand, Sammlungen, sichere Zufallsziehungen, Cooldowns und persistente Timer – sicher auch bei doppelten Browserquellen.
// 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);
});Biete Texte, Farben, Zahlen, Schalter an – der Streamer passt sie live im Overlay-Editor an, ohne Code.
// 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 */ });Streamer-Einstellungen
Deklariere Regler mit TE.defineSettings([...]), und wer das Widget nutzt, passt sie live im Overlay-Editor an – ohne Code. Jedes Feld:
Ein platziertes Widget mit einem oder mehreren 'button'-Feldern zeigt diese Aktionen in seinen Einstellungen im Overlay-Editor. Der Widget-Builder zeigt dieselben Buttons zum sicheren Testen in der Vorschau beim Programmieren.
'text'Einzeiliges Textfeldstring'number'Zahlenfeldnumber'color'FarbwählerHex-String, z. B. "#FF2E4D"'select'Dropdown (braucht options: [...])eine der Optionen'toggle'Ein/Aus-Schalterboolean'range'Schieberegler (min / max / step)number'button'Deklarativer Aktions-Buttonincrement, set oder toggle – validiertes JSON, nie Dashboard-Code'sound'Auswahl aus den hochgeladenen Sounds des Streamers + Uploaddie URL der gewählten Datei ("" = keine)'image'Auswahl aus den hochgeladenen Bildern des Streamers + Uploaddie URL der gewählten Datei ("" = keine)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();
});Regler-Definitionen sind validierte Daten. Button-action akzeptiert nur increment, set oder toggle. HTML, Callback-Code und beliebige URLs werden vom Dashboard-Renderer abgelehnt oder ignoriert.
Sandbox & Limits
Widgets laufen in einem isolierten iframe mit strikter Content-Security-Policy, sodass ein kaputtes oder bösartiges Widget nie dein Konto oder den Rest der Seite anfassen kann.
- Externe Bilder & GIFs (TikTok-Geschenk-Artwork, Avatare, jede URL)
- Google Fonts + dein eigenes @font-face
- Audio über new Audio(url)
- Anfragen an die eigenen APIs von TokElements (z. B. /api/files/…)
- CSS-Animationen, SVG, Canvas, Web Audio
- Externe <script>-Tags / CDNs
- fetch / XHR / WebSocket zu anderen Hosts
- Cookies, localStorage, Zugriff auf die Elternseite
- npm-Pakete laden
Beispiele
Komplette Widgets zum Kopieren und Einfügen. Jede Vorschau ist live – Demo-Events feuern, damit du siehst, wie es reagiert.
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;
});