Başlarken
HTML ve CSS sekmeleri widget’ını çizer. Kutu şeffaftır ve videonun üstünde durur; asla tam, opak bir arka plan boyama.
JS sekmesinde TE.on('gift', fn) ile abone ol. window.TE zaten yüklü: import yok, kurulum yok.
Çevrimdışı önizlemek için Simulate veya Fire butonlarını kullan, sonra Overlay’e ekle. Ya da Claude MCP üzerinden yapsın.
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’u bağla (MCP)
Bu widget’ları doğrudan Claude Desktop veya Claude Code’dan oluştur ve düzenle. Bir token üret, bağlayıcıyı ekle; Claude hesabında şu araçları çağırabilir:
get_docsTam yazım rehberi + tüm tetikleyiciler ve aksiyonlar (Claude önce bunu okur).list_widgetsKayıtlı widget’larını id’leriyle listeler.get_widgetBir widget’ın html / css / js’ini getirir.create_widgetAd + html/css/js ile yeni bir HTML widget’ı oluşturur.update_widgetMevcut bir widget’ı düzenler.delete_widgetBir widget’ı id ile siler.list_templatesKopyalanabilecek yerleşik şablonları listeler.get_templateBir şablonun html/css/js’ini okur.create_from_templateBir şablonu yeni bir widget’a kopyalar.list_community_templatesTopluluğun yayınladığı widget’lara göz atar.install_community_templateBir topluluk şablonunun özel, düzenlenebilir bir kopyasını kurar.TE SDK
Bir widget’ın yaptığı her şey global window.TE nesnesi üzerinden geçer.
TE.on(type, fn)Bir canlı olaya abone ol. `type` aşağıdaki tetikleyicilerden herhangi biri; `fn(ev)` olay her gerçekleştiğinde çalışır.
TE.on('*', fn)Tüm olayları yakala. `fn(ev, type)` payload’ı ve olay adını alır.
TE.off(type, fn)Daha önce kaydedilmiş bir handler’ı kaldırır.
TE.onGift(name, fn)Yalnızca adı eşleşen hediyelerde tetiklenir (büyük/küçük harf duyarsız alt dize). Her hediyeyi yakalamak için `name`’i boş bırak.
TE.onSticker(idOrUrl, fn)Yalnızca belirli bir abone emote’u / çıkartması için tetiklenir; TikTok id’si, görsel URL’si veya kaynağıyla eşleştirilir.
TE.rules.allowUser / allowGiftRoller, izin/engel listeleri, hediye adı ve minimum coin değeri için yeniden kullanılabilir uygunluk filtreleri.
TE.metricsEn güncel yayın anlık görüntüsü nesnesi (viewers, likes, coins, followers, topGifters…). TE.on('metrics', fn) ile de gelir.
TE.demoWidget’ın sergilendiği yerlerde (galeri, önizleme kutuları, oluşturucunun Demo anahtarı) true. Canlı overlay’de ve oluşturucuda ya da overlay editöründe test ederken false; orada yalnızca senin tetiklediklerin veya canlı gelenler görünür. Her widget orada kendini gösterir: gerçek olaylara benzeyen örnekleri (gerçek hediye görselleri, isimler, avatarlar) widget’ın gerçek kod yolundan geçecek şekilde bir if (TE.demo) bloğuna koy. Böyle bir bloğu olan widget host’tan önizleme olayı almaz, yani hiçbir şey iki kez tetiklenmez.
TE.defineSettings([...])Yayıncının ayarlayabileceği kontrolleri tanımlar; mevcut değerleri döndürür (varsayılanlar + yayıncının seçimleri).
TE.settingsMevcut ayar değerleri nesnesi (defineSettings’in döndürdüğüyle aynı yapı).
TE.on('settings', fn)Yayıncı bir ayarı canlı değiştirdiğinde çalışır; yeni değerlerle yeniden çiz.
TE.state.get/set/incrementToplamlar ve paylaşılan oyun durumu için Promise tabanlı atomik değerler.
TE.collection.*Sunucuda tutulan, bir kez katılınan katılımcı ve skor tabloları: join, increment, list, count, remove ve clear.
TE.queue.*Medya, istekler ve izleyicinin tetiklediği aksiyonlar için sunucuda tutulan, sınırlı FIFO kuyrukları.
TE.shared.*Birden fazla widget’ı tek bir adlandırılmış kanal durumu alanına dahil et; ilgisiz widget’lar izole kalır.
TE.random.draw(name, opts)Bir koleksiyondan güvenli şekilde bir veya daha fazla kazanan çek ve sonucu kaydet.
TE.cooldown.claim(scope, user, ms)Global veya kullanıcı başına bir bekleme süresini atomik olarak al; claimed ve retryAfterMs döndürür.
TE.timer.*Yeniden yüklemelerden etkilenmeyen, kalıcı bir gerçek zamanlı sayacı başlat, duraklat, sıfırla ve oku.
TE.points.trySpend(user, amount, reason)Puanları tek bir işlemde kontrol et ve düş; ücretli bir etkileşimi çalıştırmadan önce her zaman result.ok’a bak.
Tetikleyiciler
Dinleyebileceğin canlı olaylar. Her user şunu içerir: { id, name, username, avatar, roles }.
TE.on('gift')Bir izleyici hediye gönderdi. coins = kombonun toplamı; streakEnd kombonun bittiğini gösterir.
{ user, gift: { name, image|null, coins, repeat, combo, streakEnd } }TE.on('follow')Bir izleyici hesabı takip etti.
{ user }TE.on('subscribe')Bir izleyici abone oldu. months = art arda kaç ay.
{ user, months }TE.on('share')Bir izleyici yayını paylaştı.
{ user }TE.on('chat')Bir sohbet mesajı. emotes = bu mesajdaki satır içi abone emote görsellerinin URL’leri.
{ user, comment, emotes:[url,…] }TE.on('like')Bir izleyici beğeni gönderdi. count = bu seri; total = güncel toplam.
{ user, count, total }TE.on('join')Bir izleyici odaya girdi. isTop = en çok hediye gönderenlerden biri katıldı.
{ user, isTop }TE.on('sticker')Canlıda gönderilen bir abone emote’u veya ekran çıkartması. url = emote görseli; id = TikTok id’si.
{ user|null, id|null, url, source }TE.on('metrics')En güncel yayın anlık görüntüsü. TE.metrics bunu tutar; güncellendiğinde olay tetiklenir. hostNick ve hostAvatar yayıncının kendi adı ve profil fotoğrafıdır.
{ live, viewers, likes, followers, coins, hostNick, hostAvatar, topGifters:[…], … }TE.on('milestone')Yuvarlak bir dönüm noktası aşıldı (beğeni, coin, takipçi, abone).
{ metric, value, label }TE.on('poll')TikTok’un kendi canlı anketi: gerçek oylar, baştan sona.
{ state:'start'|'update'|'end', title, options:[{text,votes}], endsAt }TE.on('battle')LinkMic battle güncellemeleri: battle kartları, fan biletleri, ordu büyüklükleri.
{ card|null, tickets|null, armies|null, battleId|null }TE.on('rank')Saatlik sıralama konumu ve sıralama yükselme anları.
{ rank|null, from|null, to|null, countdown|null }TE.on('envelope')Canlıya kırmızı zarf / hazine sandığı bırakıldı.
{ }TE.on('pinned')Yayıncı bir yorumu sabitledi.
{ text }TE.on('deleted')Bir moderatör bir sohbet mesajını kaldırdı (id, önceki chat olayıyla eşleşir).
{ id }TE.on('streamState')Yayın canlıya geçti veya kapandı.
{ live }TE.on('apiEvent')Event API üzerinden gönderilen özel bir olay. apiEvent’i veya doğrudan özel adını dinle.
{ name, data }TE.on('nowPlaying')Yayıncının şu an çalan Spotify şarkısı VE sıradaki kuyruk. Bağlanınca ve şarkı, çalma durumu, ilerleme veya kuyruk her değiştiğinde tetiklenir. Hiçbir şey çalmıyorsa playback null olur; queue sıradaki şarkılardır.
{ 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) { /* … */ });Kanalının emote’ları widget oluşturucuda: Simulate events’i aç, Sticker’ı seç ve birini seç. Test olayı gerçek id’sini ve görsel URL’sini taşır; yani ikisinden birine bağlı bir handler gerçeğiyle test edilir.
Aksiyonlar
Widget’ının karşılık olarak neler yapabileceği: düz tarayıcı API’leri, yapıştırmaya hazır.
Ekranda bir görsel patlat: hediye/emote görseli, yüklenmiş bir dosya ya da herhangi bir URL. Birkaç saniye sonra kendiliğinden kalkar.
// 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
});Bir olayda ses çal. Bir ses ayarı sun, yayıncı kendi dosyasını seçsin; kod düzenlemek yok.
// 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(); }
});Dinamik metin yaz ve bir class’ı açıp kapatarak CSS animasyonunu yeniden tetikle.
// 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');
});Ekrandaki bir sayıyı canlı bir metriğe bağla: izleyici, beğeni, coin, takipçi. Otomatik güncellenir.
// Keep a number in sync with the live stream
TE.on('metrics', function (m) {
document.getElementById('count').textContent = m.viewers.toLocaleString();
});Bir olayı tarayıcının metin okuma özelliğiyle seslendir. Hediye veya takip teşekkürleri için harika.
// Text-to-speech shout-out
TE.on('gift', function (ev) {
var u = new SpeechSynthesisUtterance(ev.user.name + ' sent ' + ev.gift.name);
speechSynthesis.speak(u);
});Her widget galeride ve oluşturucuda kendini gösterir: gerçek olaylara benzeyen örnekler, gerçek hediye görselleri, isimler ve avatarlarla. Canlıda TE.demo false olur ve yalnızca gerçek olaylar görünür.
// 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);
}Görsel durum için basit, senkron kalıcılık. Katılımcılar, satın almalar ve paylaşılan skorlar için işlemsel runtime’ı kullan.
// 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();
});Birebir eşleşen sohbet komutları ve atomik koleksiyonlar sayesinde güvenli katılımlar, oylamalar ve çark çevirmeler mümkün.
// 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);
});
});Overlay bildirimlerini, sesleri, TTS’i, sayaçları, subathon süresini veya OBS sahne geçişlerini doğrudan bir widget’tan iste.
// One call, one stream action
TE.act({ id: 'tts', message: 'New high score!' });
TE.act({ id: 'points', user: ev.user, amount: 50 });Sadakat puanlarını işlemsel olarak kontrol et, harca veya ver, ortaya çıkan bakiyeyi al.
// 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);
});
});Atomik durum, koleksiyonlar, güvenli rastgele çekilişler, bekleme süreleri ve kalıcı sayaçlar; kopya tarayıcı kaynaklarında bile güvenli.
// 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);
});Metin, renk, sayı, açma/kapama anahtarları sun; yayıncı bunları overlay editöründe canlı ayarlar, kod gerekmez.
// 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 */ });Yayıncı ayarları
Kontrolleri TE.defineSettings([...]) ile tanımla; widget’ı kullanan kişi onları overlay editöründe canlı ayarlar, kod yok. Her alan:
Bir veya daha fazla 'button' alanı olan yerleştirilmiş bir widget, bu aksiyonları Overlay Editörü içindeki ayarlarında sunar. Widget Oluşturucu, kod yazarken güvenli önizleme testi için aynı butonları gösterir.
'text'Tek satırlık metin alanıstring'number'Sayı alanınumber'color'Renk seçicihex string, ör. "#FF2E4D"'select'Açılır liste (options: [...] gerekir)seçeneklerden biri'toggle'Açma/kapama anahtarıboolean'range'Kaydırıcı (min / max / step)number'button'Bildirimsel aksiyon butonuincrement, set veya toggle; doğrulanmış JSON, asla panel kodu değil'sound'Yayıncının yüklediği sesler için seçici + Yükleseçilen dosya URL’si ("" = yok)'image'Yayıncının yüklediği görseller için seçici + Yükleseçilen dosya URL’si ("" = yok)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();
});Kontrol tanımları doğrulanmış veridir. Buton action yalnızca increment, set veya toggle kabul eder. HTML, callback kodu ve rastgele URL’ler panel tarafından reddedilir veya yok sayılır.
Sandbox ve sınırlar
Widget’lar katı bir içerik güvenliği politikasına (CSP) sahip izole bir iframe içinde çalışır; bu yüzden bozuk veya kötü niyetli bir widget asla hesabına ya da sayfanın geri kalanına dokunamaz.
- Uzak görseller ve GIF’ler (TikTok hediye görselleri, avatarlar, herhangi bir URL)
- Google Fonts + kendi @font-face’in
- new Audio(url) ile ses
- TokElements’in kendi API’lerine istekler (ör. /api/files/…)
- CSS animasyonları, SVG, canvas, Web Audio
- Harici <script> etiketleri / CDN’ler
- Başka sunuculara fetch / XHR / WebSocket
- Çerezler, localStorage, üst sayfaya erişim
- npm paketleri yüklemek
Örnekler
Eksiksiz, kopyala-yapıştır widget’lar. Her önizleme canlı: demo olayları tetiklenir, tepki verişini izleyebilirsin.
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;
});