เริ่มต้นใช้งาน
แท็บ HTML และ CSS ใช้วาดวิดเจ็ตของคุณ กล่องวิดเจ็ตโปร่งใสและลอยอยู่เหนือวิดีโอ อย่าลงพื้นหลังทึบเต็มกล่อง
ในแท็บ JS ให้ subscribe ด้วย TE.on('gift', fn) window.TE โหลดไว้ให้แล้ว ไม่ต้อง import ไม่ต้องตั้งค่า
ใช้ Simulate หรือปุ่ม Fire เพื่อพรีวิวแบบออฟไลน์ แล้วกด Add to overlay หรือให้ Claude สร้างให้ผ่าน 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');
});เชื่อม Claude (MCP)
สร้างและแก้ไขวิดเจ็ตเหล่านี้ได้โดยตรงจาก Claude Desktop หรือ Claude Code สร้างโทเคน เพิ่มคอนเนกเตอร์ แล้ว Claude จะเรียกใช้เครื่องมือเหล่านี้กับบัญชีของคุณได้:
get_docsคู่มือการเขียนฉบับเต็ม + ทริกเกอร์และแอ็กชันทั้งหมด (Claude อ่านอันนี้ก่อน)list_widgetsแสดงรายการวิดเจ็ตที่บันทึกไว้พร้อม idget_widgetดึง html / css / js ของวิดเจ็ตหนึ่งตัวcreate_widgetสร้างวิดเจ็ต HTML ใหม่จากชื่อ + html/css/jsupdate_widgetแก้ไขวิดเจ็ตที่มีอยู่delete_widgetลบวิดเจ็ตด้วย idlist_templatesแสดงรายการเทมเพลตในตัวสำหรับคัดลอกget_templateอ่าน html/css/js ของเทมเพลตcreate_from_templateคัดลอกเทมเพลตเป็นวิดเจ็ตใหม่list_community_templatesดูวิดเจ็ตที่คอมมูนิตี้เผยแพร่install_community_templateติดตั้งสำเนาส่วนตัวที่แก้ไขได้ของเทมเพลตคอมมูนิตี้TE SDK
ทุกอย่างที่วิดเจ็ตทำจะผ่านอ็อบเจกต์ส่วนกลาง window.TE
TE.on(type, fn)Subscribe อีเวนต์สด `type` คือทริกเกอร์ใดก็ได้ด้านล่าง ส่วน `fn(ev)` จะทำงานทุกครั้งที่เกิดอีเวนต์
TE.on('*', fn)รับทุกอีเวนต์ `fn(ev, type)` จะได้รับ payload และชื่ออีเวนต์
TE.off(type, fn)ลบ handler ที่ลงทะเบียนไว้ก่อนหน้า
TE.onGift(name, fn)ทำงานเฉพาะของขวัญที่ชื่อตรงกัน (ค้นหาบางส่วนของข้อความ ไม่สนตัวพิมพ์เล็กใหญ่) ไม่ใส่ `name` เพื่อรับทุกของขวัญ
TE.onSticker(idOrUrl, fn)ทำงานเฉพาะอีโมตหรือสติกเกอร์ของผู้สมัครสมาชิกตัวใดตัวหนึ่ง จับคู่ด้วย TikTok id, URL รูปภาพ หรือแหล่งที่มา
TE.rules.allowUser / allowGiftตัวกรองสิทธิ์ที่ใช้ซ้ำได้ สำหรับบทบาท รายการอนุญาต/ปฏิเสธ ชื่อของขวัญ และมูลค่าเหรียญขั้นต่ำ
TE.metricsอ็อบเจกต์สแนปช็อตล่าสุดของสตรีม (viewers, likes, coins, followers, topGifters…) ส่งผ่าน TE.on('metrics', fn) ด้วย
TE.demoเป็น true เมื่อวิดเจ็ตถูกนำไปโชว์ (แกลเลอรี ไทล์พรีวิว สวิตช์ Demo ในตัวสร้าง) เป็น false บนโอเวอร์เลย์สด และขณะคุณทดสอบในตัวสร้างหรือตัวแก้ไขโอเวอร์เลย์ ซึ่งจะแสดงเฉพาะสิ่งที่คุณยิงเองหรือสิ่งที่เข้ามาสดเท่านั้น ทุกวิดเจ็ตต้องโชว์ตัวเองได้ที่นั่น: ใส่ตัวอย่างที่ดูเหมือนอีเวนต์จริง (ภาพของขวัญจริง ชื่อ รูปโปรไฟล์) ไว้ในบล็อก if (TE.demo) โดยผ่านโค้ดจริงของวิดเจ็ต วิดเจ็ตที่มีบล็อกแบบนี้จะไม่ได้รับอีเวนต์พรีวิวจากโฮสต์ จึงไม่มีอะไรทำงานซ้ำสองครั้ง
TE.defineSettings([...])ประกาศตัวควบคุมที่สตรีมเมอร์ปรับได้ คืนค่าปัจจุบัน (ค่าเริ่มต้นรวมกับสิ่งที่สตรีมเมอร์เลือก)
TE.settingsอ็อบเจกต์ค่าการตั้งค่าปัจจุบัน (รูปแบบเดียวกับที่ defineSettings คืนมา)
TE.on('settings', fn)ทำงานเมื่อสตรีมเมอร์เปลี่ยนการตั้งค่าสดๆ ให้เรนเดอร์ใหม่ด้วยค่าใหม่
TE.state.get/set/incrementค่าแบบ atomic ที่ใช้ Promise สำหรับยอดรวมและสถานะเกมที่ใช้ร่วมกัน
TE.collection.*ตารางผู้เข้าร่วมแบบเข้าร่วมได้ครั้งเดียวและตารางคะแนนที่เซิร์ฟเวอร์ดูแล: join, increment, list, count, remove และ clear
TE.queue.*คิว FIFO แบบจำกัดขนาดที่เซิร์ฟเวอร์ดูแล สำหรับมีเดีย คำขอ และแอ็กชันที่ผู้ชมเรียกใช้
TE.shared.*ให้วิดเจ็ตหลายตัวใช้ namespace สถานะช่องที่ตั้งชื่อร่วมกัน วิดเจ็ตที่ไม่เกี่ยวข้องยังแยกจากกัน
TE.random.draw(name, opts)สุ่มผู้ชนะหนึ่งคนหรือมากกว่าจากคอลเลกชันอย่างปลอดภัย และบันทึกผลไว้
TE.cooldown.claim(scope, user, ms)จอง cooldown แบบ global หรือรายผู้ใช้แบบ atomic คืนค่า claimed และ retryAfterMs
TE.timer.*เริ่ม หยุดชั่วคราว รีเซ็ต และอ่านตัวจับเวลาตามเวลาจริงที่บันทึกไว้และอยู่รอดแม้รีโหลด
TE.points.trySpend(user, amount, reason)ตรวจและหักแต้มในธุรกรรมเดียว ตรวจ result.ok ทุกครั้งก่อนรันการโต้ตอบที่ต้องใช้แต้ม
ทริกเกอร์
อีเวนต์สดที่คุณรับฟังได้ ทุก user มี { id, name, username, avatar, roles }
TE.on('gift')ผู้ชมส่งของขวัญ coins = ยอดรวมของคอมโบ ส่วน streakEnd บอกว่าคอมโบจบแล้ว
{ user, gift: { name, image|null, coins, repeat, combo, streakEnd } }TE.on('follow')ผู้ชมกดติดตามบัญชี
{ user }TE.on('subscribe')ผู้ชมสมัครสมาชิก months = จำนวนเดือนติดต่อกัน
{ user, months }TE.on('share')ผู้ชมแชร์ไลฟ์
{ user }TE.on('chat')ข้อความแชต emotes = URL รูปอีโมตผู้สมัครสมาชิกในข้อความนี้
{ user, comment, emotes:[url,…] }TE.on('like')ผู้ชมส่งไลก์ count = ไลก์ชุดนี้ ส่วน total = ยอดรวมสะสม
{ user, count, total }TE.on('join')ผู้ชมเข้าห้อง isTop = ผู้ส่งของขวัญสูงสุดเข้ามา
{ user, isTop }TE.on('sticker')อีโมตผู้สมัครสมาชิกหรือสติกเกอร์บนจอที่ส่งระหว่างไลฟ์ url = รูปอีโมต ส่วน id = TikTok id ของมัน
{ user|null, id|null, url, source }TE.on('metrics')สแนปช็อตล่าสุดของสตรีม เก็บไว้ใน TE.metrics และอีเวนต์จะทำงานทุกครั้งที่อัปเดต hostNick และ hostAvatar คือชื่อและรูปโปรไฟล์ของสตรีมเมอร์เอง
{ live, viewers, likes, followers, coins, hostNick, hostAvatar, topGifters:[…], … }TE.on('milestone')ข้ามหลักตัวเลขกลมๆ (ไลก์ เหรียญ ผู้ติดตาม สมาชิก)
{ metric, value, label }TE.on('poll')โพลไลฟ์ของ TikTok เอง โหวตจริง ตั้งแต่ต้นจนจบ
{ state:'start'|'update'|'end', title, options:[{text,votes}], endsAt }TE.on('battle')อัปเดตการแบตเทิล LinkMic: การ์ดแบตเทิล ตั๋วแฟน ขนาดกองทัพ
{ card|null, tickets|null, armies|null, battleId|null }TE.on('rank')อันดับในการจัดอันดับรายชั่วโมงและช่วงเวลาที่อันดับขึ้น
{ rank|null, from|null, to|null, countdown|null }TE.on('envelope')ซองแดงหรือกล่องสมบัติถูกปล่อยในไลฟ์
{ }TE.on('pinned')โฮสต์ปักหมุดคอมเมนต์
{ text }TE.on('deleted')ผู้ดูแลลบข้อความแชต (id ตรงกับอีเวนต์แชตก่อนหน้า)
{ id }TE.on('streamState')สตรีมเริ่มไลฟ์หรือออฟไลน์
{ live }TE.on('apiEvent')อีเวนต์แบบกำหนดเองที่ส่งผ่าน Event API รับฟังด้วย apiEvent หรือด้วยชื่อที่กำหนดเองโดยตรง
{ name, data }TE.on('nowPlaying')เพลง Spotify ที่สตรีมเมอร์กำลังเล่น พร้อมคิวเพลงถัดไป ทำงานเมื่อเชื่อมต่อ และทุกครั้งที่เพลง สถานะการเล่น ความคืบหน้า หรือคิวเปลี่ยน playback เป็น null เมื่อไม่มีเพลงเล่น ส่วน queue คือเพลงที่กำลังจะเล่น
{ 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) { /* … */ });อีโมตของช่องคุณอยู่ในตัวสร้างวิดเจ็ต: เปิด Simulate events เลือก Sticker แล้วเลือกหนึ่งตัว อีเวนต์ทดสอบจะมี id และ URL รูปภาพจริง handler ที่จับคู่ด้วยค่าใดค่าหนึ่งจึงได้ทดสอบกับของจริง
แอ็กชัน
สิ่งที่วิดเจ็ตของคุณทำตอบสนองได้ ใช้ API ของเบราว์เซอร์ธรรมดา พร้อมวางใช้
เด้งรูปขึ้นจอ ไม่ว่าจะเป็นภาพของขวัญ/อีโมต ไฟล์ที่อัปโหลด หรือ URL ใดก็ได้ และหายไปเองหลังไม่กี่วินาที
// 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
});เล่นเสียงเมื่อเกิดอีเวนต์ เปิดการตั้งค่าเสียงไว้ให้สตรีมเมอร์เลือกไฟล์เองได้ โดยไม่ต้องแก้โค้ด
// 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(); }
});เขียนข้อความแบบไดนามิกและเล่นแอนิเมชัน CSS ซ้ำด้วยการสลับคลาส
// 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');
});ผูกตัวเลขบนจอกับค่าสด เช่น ผู้ชม ไลก์ เหรียญ ผู้ติดตาม อัปเดตอัตโนมัติ
// Keep a number in sync with the live stream
TE.on('metrics', function (m) {
document.getElementById('count').textContent = m.viewers.toLocaleString();
});อ่านอีเวนต์ออกเสียงด้วยระบบแปลงข้อความเป็นเสียงของเบราว์เซอร์ เหมาะสำหรับชาวต์เอาต์ของขวัญหรือผู้ติดตาม
// Text-to-speech shout-out
TE.on('gift', function (ev) {
var u = new SpeechSynthesisUtterance(ev.user.name + ' sent ' + ev.gift.name);
speechSynthesis.speak(u);
});ทุกวิดเจ็ตโชว์ตัวเองในแกลเลอรีและตัวสร้าง: ตัวอย่างที่ดูเหมือนอีเวนต์จริง พร้อมภาพของขวัญ ชื่อ และรูปโปรไฟล์จริง ตอนไลฟ์ TE.demo เป็น false และจะแสดงเฉพาะอีเวนต์จริง
// 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);
}การเก็บข้อมูลแบบ synchronous อย่างง่ายสำหรับสถานะที่แสดงผล ใช้รันไทม์แบบธุรกรรมสำหรับผู้เข้าร่วม การซื้อ และคะแนนที่ใช้ร่วมกัน
// 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();
});คำสั่งแชตแบบตรงทุกตัวอักษรร่วมกับคอลเลกชันแบบ atomic ทำให้การลงชื่อ โหวต และหมุนวงล้อปลอดภัย
// 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);
});
});ขอแจ้งเตือนโอเวอร์เลย์ เสียง TTS ตัวนับ เวลา Subathon หรือสลับฉาก OBS ได้โดยตรงจากวิดเจ็ต
// One call, one stream action
TE.act({ id: 'tts', message: 'New high score!' });
TE.act({ id: 'points', user: ev.user, amount: 50 });ตรวจ ใช้ หรือให้แต้มสะสมแบบธุรกรรม และรับยอดคงเหลือที่ได้
// 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);
});
});สถานะแบบ atomic คอลเลกชัน การสุ่มที่ปลอดภัย cooldown และตัวจับเวลาที่คงอยู่ ปลอดภัยแม้มี browser source ซ้ำกัน
// 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);
});เปิดให้ปรับข้อความ สี ตัวเลข สวิตช์ สตรีมเมอร์ปรับได้สดในตัวแก้ไขโอเวอร์เลย์ ไม่ต้องใช้โค้ด
// 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 */ });การตั้งค่าสตรีมเมอร์
ประกาศตัวควบคุมด้วย TE.defineSettings([...]) แล้วใครก็ตามที่ใช้วิดเจ็ตจะปรับได้สดในตัวแก้ไขโอเวอร์เลย์ ไม่ต้องใช้โค้ด แต่ละฟิลด์:
วิดเจ็ตที่วางแล้วซึ่งมีฟิลด์ 'button' หนึ่งตัวขึ้นไป จะแสดงแอ็กชันเหล่านั้นในการตั้งค่าภายใน Overlay Editor ส่วน Widget Builder จะแสดงปุ่มเดียวกันไว้ทดสอบพรีวิวอย่างปลอดภัยระหว่างเขียนโค้ด
'text'ช่องกรอกข้อความบรรทัดเดียวstring'number'ช่องกรอกตัวเลขnumber'color'ตัวเลือกสีสตริง hex เช่น "#FF2E4D"'select'ดรอปดาวน์ (ต้องมี options: [...])หนึ่งในตัวเลือก'toggle'สวิตช์เปิด/ปิดboolean'range'แถบเลื่อน (min / max / step)number'button'ปุ่มแอ็กชันแบบประกาศincrement, set หรือ toggle — JSON ที่ผ่านการตรวจสอบ ไม่ใช่โค้ดของแดชบอร์ด'sound'ตัวเลือกจากเสียงที่สตรีมเมอร์อัปโหลด + อัปโหลดURL ของไฟล์ที่เลือก ("" = ไม่มี)'image'ตัวเลือกจากรูปที่สตรีมเมอร์อัปโหลด + อัปโหลดURL ของไฟล์ที่เลือก ("" = ไม่มี)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();
});คำนิยามตัวควบคุมเป็นข้อมูลที่ผ่านการตรวจสอบ action ของปุ่มรับเฉพาะ increment, set หรือ toggle เท่านั้น HTML โค้ด callback และ URL ใดๆ จะถูกปฏิเสธหรือเพิกเฉยโดยตัวเรนเดอร์ของแดชบอร์ด
แซนด์บ็อกซ์และข้อจำกัด
วิดเจ็ตรันใน iframe ที่แยกออกมาพร้อม content-security policy ที่เข้มงวด วิดเจ็ตที่พังหรือเป็นอันตรายจึงแตะต้องบัญชีของคุณหรือส่วนอื่นของหน้าไม่ได้เลย
- รูปภาพและ GIF จากภายนอก (ภาพของขวัญ TikTok รูปโปรไฟล์ URL ใดก็ได้)
- Google Fonts + @font-face ของคุณเอง
- เสียงผ่าน new Audio(url)
- คำขอไปยัง API ของ TokElements เอง (เช่น /api/files/…)
- แอนิเมชัน CSS, SVG, canvas, Web Audio
- แท็ก <script> ภายนอก / CDN
- fetch / XHR / WebSocket ไปยังโฮสต์อื่น
- คุกกี้, localStorage, การเข้าถึงหน้าแม่
- การโหลดแพ็กเกจ npm
ตัวอย่าง
วิดเจ็ตครบชุด คัดลอกไปวางได้เลย ทุกพรีวิวทำงานสด อีเวนต์เดโมจะยิงให้คุณดูการตอบสนอง
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;
});