Widget SDK
Make your own overlays in HTML, CSS and JavaScript. Your widget gets every gift, like, follow and chat message of your LIVE on TikTok, Twitch, YouTube and Kick, in English and Arabic, and shows on stream like any CheerQuake overlay.
Start in a minute
- 1
Open My widgets
In CheerQuake: Overlays → My widgets → New widget.
- 2
Pick a template
Gift rocket, Chat bubbles, Top gifters, Goal, Countdown, Chat vote, Hype meter… or a blank widget, or one from the community gallery.
- 3
Change it
Its settings on its page, or anything at all with Edit code. The preview shows your changes at once.
- 4
Put it on stream
Copy its link into TikTok LIVE Studio (Add source → Link) or OBS (Sources → + → Browser), or add it to a Scene.
What a widget is made of
A widget is a small web page. The editor has five tabs:
| HTML | What the widget shows. {{id}} puts a setting’s value here. |
|---|---|
| CSS | How it looks. {{id}} works here too, e.g. color: {{accent}};. |
| JavaScript | What it does. CQ is the SDK, ready before your code runs. |
| Settings fields | The settings the streamer gets on the widget’s page (a list in JSON). |
| Details | Its name, author, description and size. |
Events
Call CQ.on(type, fn) to hear an event. It returns a function that stops listening.
CQ.on('gift', (ev) => {
console.log(ev.user.name, ev.amount, ev.gift.name, ev.coins);
});'gift' | A gift. A streak sends several events: amount is what this one adds, total the streak so far. |
|---|---|
'follow' | A new follower. |
'like' | Likes: amount likes. |
'share' | The LIVE was shared. |
'chat' | A chat message: text. |
'subscribe' | A subscription (TikTok, Twitch, YouTube, Kick). |
'event' | Every one of the above. |
'alert' | An alert CheerQuake shows (its title, message, picture…). |
'goal' | A goal was reached: { id, target }. |
'button' | A Button setting was pressed: its id. |
'settings' | The streamer changed the settings. Without this, the widget starts again with them. |
'state' | New LIVE numbers (CQ.state). |
'message' | Every message CheerQuake sends to overlays (timer, wheel, poll, battle…). |
'ready' | The page is ready. |
The event
Every viewer event has the same shape:
ev.type | gift, follow, like, share, chat or subscribe |
|---|---|
ev.user | { name, username, avatar, rank } |
ev.gift | { name, image, emoji, coins } (only for gifts; image can be empty: show the emoji) |
ev.amount | Gifts or likes in this event (1 for the others) |
ev.total | Gifts so far in this streak |
ev.coins | Coins this event adds (amount × gift coins) |
ev.text | The chat message |
ev.platform | tiktok, twitch, youtube, kick or bot |
ev.test | true for test and sample events |
ev.streak | The same for every event of one gift streak |
CQ: everything your code can use
CQ.on(type, fn) · CQ.off(type, fn) | Listen for an event, or stop. |
|---|---|
CQ.settings | The settings, by id: CQ.settings.title. |
CQ.state | The LIVE’s numbers (below). |
CQ.lang · CQ.dir | en or ar; ltr or rtl. |
CQ.T(english, arabic) | The text in CheerQuake’s language. CQ.T({ en, ar }) works too. |
CQ.store.get(key, otherwise) · set(key, value) · remove(key) · clear() | Small things kept between reloads (100 KB at most), by the browser source that shows the widget. |
CQ.queue(async () => …) | Jobs one after another, like alerts that wait their turn. |
CQ.wait(ms) | A pause: await CQ.wait(1500). |
CQ.play(url, volume) | Plays a sound, e.g. a Sound setting. |
CQ.giftPicture(gift, size) · CQ.avatar(user, size) | A gift’s or a viewer’s picture, ready to add to the page. |
CQ.el(tag, class, text) | A new element. |
CQ.fmt(n) | A number with Western digits: 1,234. |
CQ.esc(text) | Text made safe for innerHTML. |
CQ.bidi(text) | A name or message kept in its own direction inside other text. |
CQ.isRtl(text) | Does the text start with a right-to-left letter? |
CQ.font(name) | Loads a Google Font and gives its font-family. |
CQ.test(type) | A sample event to this widget only. |
CQ.log(…) | A line in the editor’s Console. |
CQ.demo | true in previews, where sample events come by themselves. |
CQ.widget | { id, name, width, height }. |
CQ.state: the LIVE’s numbers
CQ.state comes a moment after the start and changes during the LIVE:
CQ.state.live | Is a LIVE on? |
|---|---|
CQ.state.session | { viewers, likes, coins, gifts, follows, shares, subs, chats, startedAt } |
CQ.state.top | Top gifters: [{ name, avatar, coins, … }] |
CQ.state.boards | { coins, likes, shares, chats }: the leaderboards |
CQ.state.goals | [{ id, name, current, target }] |
CQ.state.recent | The latest events, in the event shape |
CQ.state.last | The last follower, gifter, sharer… |
timer · wins · points | The subathon timer, the win goal, CQ Points |
Settings
Settings fields are a list. Each has an id (the name in CQ.settings), a type, a label and a default. A label and a text default can be in both languages.
text · textarea | Text |
|---|---|
number | A number (min, max, step) |
slider | A slider (min, max, step) |
color | A color: #ff8a1f |
toggle | On or off |
select | A choice: options: [["top", "Top"], ["bottom", "Bottom"]] |
image · video | A picture or video: CheerQuake’s pictures or the streamer’s own |
sound | A sound from CheerQuake’s library or the streamer’s own |
font | A Google Font (Cairo, Tajawal, Changa, Lalezar…), loaded for you |
button | A button on the widget’s page: your code hears it with CQ.on('button') |
heading | A title between settings |
[
{ "id": "title", "type": "text", "label": { "en": "Title", "ar": "العنوان" },
"default": { "en": "Gifts:", "ar": "الهدايا:" } },
{ "id": "color", "type": "color", "label": "Color", "default": "#ff8a1f" },
{ "id": "font", "type": "font", "label": "Font", "default": "Cairo" },
{ "id": "reset", "type": "button", "label": { "en": "Reset", "ar": "إعادة الضبط" } }
]A whole widget
A gift counter that remembers its count, in about 20 lines:
<div class="box">{{title}} <b id="n">0</b></div>.box {
padding: 14px 18px;
border-radius: 16px;
background: rgba(10, 16, 36, 0.9);
color: #fff;
font: 800 28px "{{font}}", sans-serif;
}
.box b { color: {{color}}; }const n = document.getElementById('n');
let gifts = CQ.store.get('gifts', 0);
n.textContent = CQ.fmt(gifts);
CQ.on('gift', (ev) => {
gifts += ev.amount;
CQ.store.set('gifts', gifts);
n.textContent = CQ.fmt(gifts);
});
// A "Reset" button in the settings.
CQ.on('button', (id) => {
if (id !== 'reset') return;
gifts = 0;
CQ.store.set('gifts', 0);
n.textContent = '0';
});Arabic and right to left
CheerQuake is made for Arabic and English. A few habits make a widget look right in both:
- Write every text with
CQ.T('Thank you', 'شكرًا'), or a setting with a default in both languages. - The widget’s page already has
langanddirset: usemargin-inline-start,text-align: startand flexbox, not left and right. - Give names and messages
dir="auto"(orCQ.bidi()inside a sentence): an English name stays in place inside an Arabic text. CQ.fmt()keeps Western digits (1,234) in both languages.- Arabic fonts: Cairo, Tajawal, Almarai, Changa, El Messiri, Lalezar, Reem Kufi, Amiri, Baloo Bhaijaan 2.
title.textContent = CQ.T('Thank you!', 'شكرًا لك!');
name.dir = 'auto';
line.innerHTML = `${CQ.esc(CQ.bidi(ev.user.name))} ${CQ.T('sent', 'أرسل')} ${CQ.fmt(ev.amount)}`;The community gallery
- Community gallery on My widgets is where streamers share their widgets with everyone. Search it, filter by category, sort by newest, most added or most liked, and see each widget run in its preview.
- Add puts a copy in My widgets: change its settings or its code as you like, it’s yours.
- To share yours, open it and click Share in the gallery: its name, your name as its author (never your email), a category and a description. Change it later and Update in the gallery; Remove from the gallery takes it out (people who added it keep their copy).
- Like the widgets you love. Report one that is hurtful, adult, copied or broken: a widget several streamers report is hidden until we look at it. At most 20 shares a day and 100 widgets per streamer.
Test it
The editor’s preview gets sample events by themselves (switch off Sample events to stop them) and the Send a test buttons send one of each kind. Errors and console.log lines show in the Console, with the JavaScript line: click it to go there. Your real LIVE’s events reach the preview too.
Safety and limits
- A widget runs in a sandbox: it can’t reach the page around it, CheerQuake, other widgets or the streamer’s PC.
- It can’t load scripts from other sites, connect anywhere (fetch, WebSocket) or run code from text (eval). Put all your code in the JavaScript tab.
- Pictures, sounds, videos and Google Fonts load from anywhere.
- At most 300 KB of HTML, CSS and JavaScript each, 80 settings, and 100 KB in CQ.store.
- On stream, a widget’s errors never show: they show in previews and the editor’s Console.