Primeiros passos
As abas HTML e CSS desenham seu widget. A caixa é transparente e flutua sobre o vídeo — nunca pinte um fundo opaco em tela cheia.
Na aba JS, assine com TE.on('gift', fn). O window.TE já está carregado — sem imports, sem configuração.
Use Simular ou os botões Disparar para ver a prévia offline, depois Adicionar ao overlay — ou deixe o Claude criar via 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');
});Conectar o Claude (MCP)
Crie e edite estes widgets direto do Claude Desktop ou do Claude Code. Gere um token, adicione o conector, e o Claude pode chamar estas ferramentas na sua conta:
get_docsO guia completo de criação + todos os gatilhos e ações (o Claude lê isto primeiro).list_widgetsLista seus widgets salvos com os ids.get_widgetBusca o html / css / js de um widget.create_widgetCria um novo widget HTML a partir de nome + html/css/js.update_widgetEdita um widget existente.delete_widgetApaga um widget pelo id.list_templatesLista os templates embutidos para copiar.get_templateLê o html/css/js de um template.create_from_templateCopia um template para um novo widget.list_community_templatesNavega pelos widgets publicados pela comunidade.install_community_templateInstala um fork privado e editável de um template da comunidade.O SDK TE
Tudo o que um widget faz passa pelo objeto global window.TE.
TE.on(type, fn)Assina um evento ao vivo. `type` é qualquer gatilho abaixo; `fn(ev)` roda toda vez que ele acontece.
TE.on('*', fn)Pega todos os eventos. `fn(ev, type)` recebe o payload e o nome do evento.
TE.off(type, fn)Remove um handler registrado antes.
TE.onGift(name, fn)Dispara só para presentes cujo nome bate (trecho, sem diferenciar maiúsculas). Omita `name` para pegar todos os presentes.
TE.onSticker(idOrUrl, fn)Dispara só para um emote / sticker de assinante específico — identificado pelo id do TikTok, URL da imagem ou origem.
TE.rules.allowUser / allowGiftFiltros de elegibilidade reutilizáveis para cargos, listas de permissão/bloqueio, nome do presente e valor mínimo em moedas.
TE.metricsO objeto com o snapshot mais recente da live (viewers, likes, coins, followers, topGifters…). Também entregue via TE.on('metrics', fn).
TE.demoVerdadeiro onde o widget está em exibição (galeria, miniaturas de prévia, a chave Demo do builder). Falso num overlay ao vivo e enquanto você testa no builder ou no editor de overlay, onde só aparece o que você dispara ou o que chega ao vivo. Todo widget se mostra ali: coloque exemplos que parecem eventos reais (arte real dos presentes, nomes, avatares) num bloco if (TE.demo), pelo caminho de código real do widget. Um widget com esse bloco não recebe eventos de prévia do host, então nada dispara duas vezes.
TE.defineSettings([...])Declara controles que o streamer pode ajustar; retorna os valores atuais (padrões mesclados com as escolhas do streamer).
TE.settingsO objeto com os valores atuais das configurações (mesmo formato que defineSettings retornou).
TE.on('settings', fn)Roda quando o streamer muda uma configuração ao vivo — renderize de novo com os novos valores.
TE.state.get/set/incrementValores atômicos baseados em Promise para totais e estado compartilhado de jogos.
TE.collection.*Participantes de entrada única e tabelas de pontuação guardados no servidor: join, increment, list, count, remove e clear.
TE.queue.*Filas FIFO limitadas no servidor para mídia, pedidos e ações disparadas por viewers.
TE.shared.*Coloca vários widgets num mesmo namespace de estado do canal; widgets não relacionados continuam isolados.
TE.random.draw(name, opts)Sorteia com segurança um ou mais vencedores de uma coleção e registra o resultado.
TE.cooldown.claim(scope, user, ms)Reivindica atomicamente um cooldown global ou por usuário; retorna claimed e retryAfterMs.
TE.timer.*Inicia, pausa, reinicia e lê um timer de relógio persistente que sobrevive a recarregamentos.
TE.points.trySpend(user, amount, reason)Verifica e debita pontos numa única transação; sempre confira result.ok antes de rodar uma interação paga.
Gatilhos
Eventos ao vivo que você pode escutar. Todo user contém { id, name, username, avatar, roles }.
TE.on('gift')Um viewer enviou um presente. coins = total do combo; streakEnd marca o fim do combo.
{ user, gift: { name, image|null, coins, repeat, combo, streakEnd } }TE.on('follow')Um viewer seguiu a conta.
{ user }TE.on('subscribe')Um viewer assinou. months = quantos meses seguidos.
{ user, months }TE.on('share')Um viewer compartilhou a live.
{ user }TE.on('chat')Uma mensagem do chat. emotes = URLs das imagens de emotes de assinante nesta mensagem.
{ user, comment, emotes:[url,…] }TE.on('like')Um viewer mandou likes. count = esta rajada; total = total acumulado.
{ user, count, total }TE.on('join')Um viewer entrou na sala. isTop = entrou um dos maiores doadores.
{ user, isTop }TE.on('sticker')Um emote de assinante ou sticker na tela enviado ao vivo. url = a imagem do emote; id = o id dele no TikTok.
{ user|null, id|null, url, source }TE.on('metrics')O snapshot mais recente da live. TE.metrics guarda ele; o evento dispara sempre que atualiza. hostNick e hostAvatar são o nome e a foto de perfil do próprio streamer.
{ live, viewers, likes, followers, coins, hostNick, hostAvatar, topGifters:[…], … }TE.on('milestone')Um marco redondo foi ultrapassado (likes, moedas, seguidores, assinantes).
{ metric, value, label }TE.on('poll')A enquete nativa da live do TikTok — votos reais, do início ao fim.
{ state:'start'|'update'|'end', title, options:[{text,votes}], endsAt }TE.on('battle')Atualizações de batalha LinkMic: cartas de batalha, fan tickets, tamanho dos exércitos.
{ card|null, tickets|null, armies|null, battleId|null }TE.on('rank')Posição no ranking por hora e momentos de subida.
{ rank|null, from|null, to|null, countdown|null }TE.on('envelope')Um envelope vermelho / baú do tesouro caiu na live.
{ }TE.on('pinned')O host fixou um comentário.
{ text }TE.on('deleted')Um moderador removeu uma mensagem do chat (id bate com o evento de chat anterior).
{ id }TE.on('streamState')A live ficou online ou offline.
{ live }TE.on('apiEvent')Um evento personalizado enviado pela Event API. Escute apiEvent ou direto o nome personalizado dele.
{ name, data }TE.on('nowPlaying')A música atual do Spotify do streamer MAIS a fila do que vem a seguir. Dispara ao conectar e sempre que a música, o estado de reprodução, o progresso ou a fila mudam. playback é null quando nada está tocando; queue são as próximas faixas.
{ 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) { /* … */ });Os emotes do seu canal ficam no criador de widgets: abra Simular eventos, escolha Sticker e selecione um. O evento de teste traz o id e a URL da imagem reais, então um handler baseado em qualquer um dos dois é testado com a coisa real.
Ações
O que o seu widget pode fazer em resposta — APIs comuns do navegador, prontas para colar.
Coloque uma imagem na tela — a arte do presente/emote, um arquivo enviado ou qualquer URL. Some sozinha depois de alguns segundos.
// 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
});Toque um áudio num evento. Exponha uma configuração de som para o streamer escolher o próprio arquivo — sem editar código.
// 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(); }
});Escreva texto dinâmico e dispare de novo uma animação CSS alternando uma classe.
// 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');
});Ligue um número na tela a uma métrica ao vivo — viewers, likes, moedas, seguidores — atualizado automaticamente.
// Keep a number in sync with the live stream
TE.on('metrics', function (m) {
document.getElementById('count').textContent = m.viewers.toLocaleString();
});Fale um evento com o texto para fala do navegador. Ótimo para agradecer presentes ou follows.
// Text-to-speech shout-out
TE.on('gift', function (ev) {
var u = new SpeechSynthesisUtterance(ev.user.name + ' sent ' + ev.gift.name);
speechSynthesis.speak(u);
});Todo widget se mostra na galeria e no builder: exemplos que parecem eventos reais, com arte real dos presentes, nomes e avatares. Ao vivo, TE.demo é falso e só aparecem eventos reais.
// 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);
}Persistência síncrona simples para estado visual. Use o runtime transacional para participantes, compras e pontuações compartilhadas.
// 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();
});Comandos de chat com correspondência exata mais coleções atômicas permitem inscrições, votos e giros seguros.
// 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);
});
});Peça alertas do overlay, sons, TTS, contadores, tempo de subathon ou troca de cena no OBS direto de um widget.
// One call, one stream action
TE.act({ id: 'tts', message: 'New high score!' });
TE.act({ id: 'points', user: ev.user, amount: 50 });Verifique, gaste ou dê pontos de fidelidade de forma transacional e receba o saldo resultante.
// 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);
});
});Estado atômico, coleções, sorteios seguros, cooldowns e timers persistentes — seguros mesmo com fontes de navegador duplicadas.
// 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);
});Exponha textos, cores, números, chaves — o streamer ajusta ao vivo no editor de overlay, sem código.
// 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 */ });Configurações do streamer
Declare controles com TE.defineSettings([...]) e quem usar o widget ajusta ao vivo no editor de overlay — sem código. Cada campo:
Um widget posicionado com um ou mais campos 'button' expõe essas ações nas configurações dele dentro do Editor de overlay. O Widget Builder mostra os mesmos botões para testar a prévia com segurança enquanto você programa.
'text'Campo de texto de uma linhastring'number'Campo numériconumber'color'Seletor de corstring hex, ex. "#FF2E4D"'select'Lista suspensa (precisa de options: [...])uma das opções'toggle'Chave liga/desligaboolean'range'Slider (min / max / step)number'button'Botão de ação declarativoincrement, set ou toggle — JSON validado, nunca código do painel'sound'Seletor dos sons enviados pelo streamer + Uploada URL do arquivo escolhido ("" = nenhum)'image'Seletor das imagens enviadas pelo streamer + Uploada URL do arquivo escolhido ("" = nenhum)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();
});As definições de controles são dados validados. O action de um botão aceita só increment, set ou toggle. HTML, código de callback e URLs arbitrárias são rejeitados ou ignorados pelo renderizador do painel.
Sandbox e limites
Os widgets rodam num iframe isolado com uma política de segurança de conteúdo rígida, então um widget quebrado ou malicioso nunca consegue tocar na sua conta ou no resto da página.
- Imagens e GIFs remotos (arte de presentes do TikTok, avatares, qualquer URL)
- Google Fonts + seu próprio @font-face
- Áudio via new Audio(url)
- Requisições às APIs do próprio TokElements (ex. /api/files/…)
- Animações CSS, SVG, canvas, Web Audio
- Tags <script> externas / CDNs
- fetch / XHR / WebSocket para outros hosts
- Cookies, localStorage, acesso à página pai
- Carregar pacotes npm
Exemplos
Widgets completos, para copiar e colar. Cada prévia é ao vivo — eventos de demo disparam para você ver a reação.
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;
});