chat-aggregator

Twitch and YouTube live chat in one overlay for your stream.

View the Project on GitHub iSneeze/chat-aggregator

Overlay themes

A theme decides how the chat looks on stream. chat-aggregator comes with two built-in themes; your own themes are folders you can edit with any text editor, share, and switch between in the app.

Built-in themes

Pick them in the app’s theme list, or theme = "minimal" in config.toml. They’re built into the app and improve with its updates, so they can’t be edited in place; New theme… starts a folder of your own from a copy of Default.

Where themes live

In the themes folder next to chat-aggregator’s settings (the app’s Open themes folder button takes you there):

themes/
├── cozy/
│   ├── overlay.css     the look: colours, fonts, sizes, animations
│   ├── message.html    optional: the structure of one message
│   └── bg.png, …       optional: images and fonts the CSS uses
└── minimal/
    └── overlay.css

The folder name is the theme’s name. Every file is optional: anything a theme doesn’t have comes from the built-in theme. Most themes only need overlay.css. (A theme folder somewhere else works too: put its full path in config.toml, e.g. theme = "/home/me/obs/my-theme".)

Making a theme

  1. In the app: New theme…, give it a name, Create. This copies the built-in overlay.css and message.html into a new folder and switches the overlay to it.
  2. Open themes folder, open the new folder, edit overlay.css.
  3. Reload overlays in the app: OBS shows the change right away.

Start with the variables at the top of overlay.css: fonts, colours, card style and sizes are all set there. Two of them control the overlay’s behaviour instead of its look:

Chat direction

Where new messages appear is a scene choice rather than a theme choice, so it isn’t set in the theme:

With the newest at the top, the chat element gets the class chat--newest-top; the built-in theme uses it to let messages slide in from above instead of below (--msg-enter-offset). Themes made before this option existed work too: the overlay flips the direction itself.

A theme per browser source

Normally every overlay shows the theme picked in the app. To give one OBS scene its own theme, add ?theme= and the theme’s name to its overlay URL:

http://127.0.0.1:7878/?theme=cozy
http://127.0.0.1:7878/?theme=cozy&newest=top
http://127.0.0.1:7878/?theme=minimal     a built-in theme (or default)

That overlay keeps its theme whatever the app is set to. Reload overlays in the app still reloads it (with its own theme), so editing its files works as usual. A name with a space goes into the URL as %20 (?theme=my%20theme). If the name doesn’t match a theme folder, the overlay shows the app’s theme instead, and the log says why.

Images and fonts

Put them into the theme folder and use relative paths in overlay.css:

@font-face {
  font-family: "My Font";
  src: url(fonts/MyFont.woff2);
}
.chat { background: url(bg.png); }

Changing the structure (message.html)

message.html is a MiniJinja template for one message. The comment at the top of the built-in one lists every value it can use (author, badges, emotes, amounts, …); the classes it sets (msg--donation, msg--twitch, badge--moderator, …) are what the CSS styles. Keep the root element’s msg class and its data-id, data-platform and data-author attributes: the overlay needs them to remove deleted messages.

Colours by amount

Paid messages (Super Chats, Super Stickers, bits) get a class for how big the amount is: msg--tier-1, msg--tier-2, … Each platform has its own scale, so style the tier together with the platform:

platform tiers
YouTube 1 blue, 2 cyan, 3 teal, 4 yellow, 5 orange, 6 magenta, 7 red: YouTube’s own Super Chat tiers, whatever the currency
Twitch bits: 1 (1+), 2 (100+), 3 (1,000+), 4 (5,000+), 5 (10,000+): Twitch’s cheer steps
.msg--youtube.msg--tier-1 { --paid-color: #1e88e5; }
.msg--youtube.msg--tier-4 { --paid-color: #ffca28; }
.msg--twitch.msg--tier-2  { --paid-color: #9c3ee8; }

Messages without a tier (YouTube jewel gifts, the app’s test messages) have no tier class, so give --paid-color a default too.

A look per chatter

Every chatter can get something of their own (a colour, a shape, a little marker next to the name) that stays the same every time they write, across streams and name changes. The seed filter turns their id into a number:

<span class="marker marker--shape-{{ author.id | seed(6, 'shape') }}"
      style="--marker-hue: {{ author.id | seed(360, 'hue') }}"></span>

value | seed(n, 'salt') gives a number from 0 to n−1: seed(6, …) picks one of six shapes (style .marker--shape-0 to .marker--shape-5), seed(360, …) a hue for hsl(var(--marker-hue) 80% 60%) or filter: hue-rotate(…). Each salt ('shape', 'hue', any word) gives an independent number, so shape and colour don’t go hand in hand. The numbers never change between versions of chat-aggregator.

Good to know: it’s the same person on Twitch and YouTube, but two ids, so two looks. And two chatters can land on the same combination; the more choices you combine (shapes × colours × sizes …), the rarer that gets.

Safety

Chat text is always escaped, so whatever chatters type can never break the page or run code in it. Two rules keep it that way:

And keep OBS up to date: its embedded browser gets security fixes with it.