CheerQuake
For streamers and designers

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.

15ready-made templates
13kinds of settings
0servers to run

Start in a minute

  1. 1

    Open My widgets

    In CheerQuake: Overlays → My widgets → New widget.

  2. 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. 3

    Change it

    Its settings on its page, or anything at all with Edit code. The preview shows your changes at once.

  4. 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:

HTMLWhat the widget shows. {{id}} puts a setting’s value here.
CSSHow it looks. {{id}} works here too, e.g. color: {{accent}};.
JavaScriptWhat it does. CQ is the SDK, ready before your code runs.
Settings fieldsThe settings the streamer gets on the widget’s page (a list in JSON).
DetailsIts 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.typegift, 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.amountGifts or likes in this event (1 for the others)
ev.totalGifts so far in this streak
ev.coinsCoins this event adds (amount × gift coins)
ev.textThe chat message
ev.platformtiktok, twitch, youtube, kick or bot
ev.testtrue for test and sample events
ev.streakThe 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.settingsThe settings, by id: CQ.settings.title.
CQ.stateThe LIVE’s numbers (below).
CQ.lang · CQ.diren 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.demotrue 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.liveIs a LIVE on?
CQ.state.session{ viewers, likes, coins, gifts, follows, shares, subs, chats, startedAt }
CQ.state.topTop gifters: [{ name, avatar, coins, … }]
CQ.state.boards{ coins, likes, shares, chats }: the leaderboards
CQ.state.goals[{ id, name, current, target }]
CQ.state.recentThe latest events, in the event shape
CQ.state.lastThe last follower, gifter, sharer…
timer · wins · pointsThe 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 · textareaText
numberA number (min, max, step)
sliderA slider (min, max, step)
colorA color: #ff8a1f
toggleOn or off
selectA choice: options: [["top", "Top"], ["bottom", "Bottom"]]
image · videoA picture or video: CheerQuake’s pictures or the streamer’s own
soundA sound from CheerQuake’s library or the streamer’s own
fontA Google Font (Cairo, Tajawal, Changa, Lalezar…), loaded for you
buttonA button on the widget’s page: your code hears it with CQ.on('button')
headingA title between settings
Settings fields
[
  { "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:

HTML
<div class="box">{{title}} <b id="n">0</b></div>
CSS
.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}}; }
JavaScript
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 lang and dir set: use margin-inline-start, text-align: start and flexbox, not left and right.
  • Give names and messages dir="auto" (or CQ.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)}`;

Share a widget

On a widget’s page, Export… saves it as one .cqwidget file: its code, its settings fields and its settings. Import… on My widgets adds a file someone shared with you. The file is JSON:

.cqwidget
{
  "format": "cheerquake-widget",
  "formatVersion": 1,
  "name": "Gift counter",
  "author": "Your name",
  "description": "…",
  "version": "1.0",
  "size": { "w": 600, "h": 300 },
  "html": "…", "css": "…", "js": "…",
  "fields": [ … ],
  "values": { "title": "Gifts:" }
}

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.