はじめに
HTMLとCSSのタブでウィジェットを描きます。枠は透明で映像の上に浮かぶので、不透明な全面背景は絶対に塗らないでください。
JSタブでTE.on('gift', fn)を使って購読します。window.TEは読み込み済みなので、importも準備も不要です。
Simulate(シミュレート)やFire(発火)ボタンでオフラインでプレビューし、オーバーレイに追加します。MCP経由でClaudeに作ってもらうこともできます。
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保存したウィジェットをidとともに一覧表示。get_widgetひとつのウィジェットのhtml / css / jsを取得。create_widget名前とhtml/css/jsから新しいHTMLウィジェットを作成。update_widget既存のウィジェットを編集。delete_widgetidを指定してウィジェットを削除。list_templatesコピー元にできる内蔵テンプレートを一覧表示。get_templateテンプレートのhtml/css/jsを読む。create_from_templateテンプレートを新しいウィジェットにコピー。list_community_templatesコミュニティが公開したウィジェットを閲覧。install_community_templateコミュニティテンプレートの非公開・編集可能なフォークをインストール。TE SDK
ウィジェットの動作はすべてグローバルなwindow.TEオブジェクトを通して行います。
TE.on(type, fn)ライブイベントを購読します。`type`は下記のいずれかのトリガーで、`fn(ev)`はイベントが起きるたびに実行されます。
TE.on('*', fn)すべてのイベントを受け取ります。`fn(ev, type)`はペイロードとイベント名を受け取ります。
TE.off(type, fn)以前に登録したハンドラーを解除します。
TE.onGift(name, fn)名前が一致するギフト(大文字小文字を区別しない部分一致)でのみ発火します。`name`を省略するとすべてのギフトを受け取ります。
TE.onSticker(idOrUrl, fn)特定のサブスクエモート/ステッカーひとつでのみ発火します。TikTokのid、画像URL、またはsourceで照合します。
TE.rules.allowUser / allowGiftロール、許可/拒否リスト、ギフト名、最低コイン数による再利用可能な参加条件フィルター。
TE.metrics最新の配信スナップショットオブジェクト(viewers、likes、coins、followers、topGifters…)。TE.on('metrics', fn)でも受け取れます。
TE.demoウィジェットが見本として表示される場所(ギャラリー、プレビュータイル、ビルダーのDemoスイッチ)ではtrue。ライブのオーバーレイ上や、ビルダー・オーバーレイエディターでテストしている間はfalseで、自分で発火したものか実際に届いたものだけが表示されます。どのウィジェットもそこで自分自身を見せるので、実際のイベントに見えるサンプル(本物のギフト画像、名前、アイコン)をif (TE.demo)ブロックに入れ、ウィジェット本来のコードパスで流してください。このブロックを持つウィジェットにはホストからプレビューイベントが送られないため、二重に発火することはありません。
TE.defineSettings([...])配信者が調整できるコントロールを宣言し、現在の値(デフォルトと配信者の選択をマージしたもの)を返します。
TE.settings現在の設定値オブジェクト(defineSettingsが返したものと同じ形)。
TE.on('settings', fn)配信者がライブ中に設定を変更したときに実行されます。新しい値で再描画してください。
TE.state.get/set/increment合計値や共有ゲーム状態のための、Promiseベースのアトミックな値。
TE.collection.*サーバー管理の一度きり参加者リストとスコア表:参加、加算、一覧、件数、削除、クリア。
TE.queue.*メディア、リクエスト、視聴者が発動するアクション用の、上限付きサーバー管理FIFOキュー。
TE.shared.*複数のウィジェットをひとつの名前付きチャンネル状態の名前空間に参加させます。無関係なウィジェットは分離されたままです。
TE.random.draw(name, opts)コレクションから1人以上の当選者を安全に抽選し、結果を記録します。
TE.cooldown.claim(scope, user, ms)グローバルまたはユーザーごとのクールダウンをアトミックに取得し、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は以前のchatイベントと一致)。
{ 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) { /* … */ });チャンネルのエモートはウィジェットビルダーにあります。イベントをシミュレートを開き、ステッカーを選んでひとつ選択してください。テストイベントには実際のidと画像URLが入っているので、どちらをキーにしたハンドラーも本物でテストできます。
アクション
ウィジェットが反応としてできること。普通のブラウザ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
});イベントで音声を再生。sound設定を用意すれば、配信者がコードを編集せずに自分のファイルを選べます。
// 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);
}見た目の状態のためのシンプルな同期型の永続化。参加者、購入、共有スコアにはトランザクション対応のランタイムを使ってください。
// 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();
});完全一致のチャットコマンドとアトミックなコレクションで、安全な参加受付、投票、ルーレットが実現できます。
// 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、カウンター、耐久配信の時間追加、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);
});
});アトミックな状態、コレクション、安全な抽選、クールダウン、永続タイマー。ブラウザソースが重複していても安全です。
// 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'フィールドをひとつ以上持つ配置済みウィジェットは、オーバーレイエディター内の設定でそのアクションを表示します。ウィジェットビルダーでも、コーディング中に安全にプレビューテストできるよう同じボタンが表示されます。
'text'1行のテキスト入力string'number'数値入力number'color'カラーピッカーhex文字列(例:"#FF2E4D")'select'ドロップダウン(options: [...]が必要)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、コールバックコード、任意のURLは、ダッシュボードのレンダラーで拒否または無視されます。
サンドボックスと制限
ウィジェットは厳格なコンテンツセキュリティポリシーを持つ分離されたiframe内で動くため、壊れたウィジェットや悪意あるウィジェットがあなたのアカウントやページの他の部分に触れることはありません。
- 外部の画像とGIF(TikTokギフト画像、アイコン、任意のURL)
- Google Fonts+独自の@font-face
- new Audio(url)による音声
- TokElements自身のAPIへのリクエスト(例:/api/files/…)
- CSSアニメーション、SVG、canvas、Web Audio
- 外部の<script>タグ/CDN
- 他のホストへのfetch / XHR / WebSocket
- Cookie、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;
});