Skip to content

Repository files navigation

BepChat

One chat overlay for Twitch, Kick, YouTube and TikTok.

BepChat merges live chat from every platform you stream on into a single transparent page you can drop into OBS as a browser source. It renders 7TV, BetterTTV and FrankerFaceZ emotes on all four platforms, shows real badge art and each service's own logo beside its messages, and supports 7TV name paints.

There is no account system. All configuration lives in the overlay URL, so the public instance stores nothing about you and a self-hosted instance needs no database.

Try it

Build an overlay URL at chat.dek.to, then add it in OBS under Sources → + → Browser. The background is transparent, so no chroma key is needed.

How each platform connects

Platform Transport Credentials
Twitch Anonymous IRC over WebSocket, direct from the browser None
Kick Kick's public Pusher feed, direct from the browser None
YouTube Relayed by the server over SSE, using YouTube's internal live chat API None
TikTok Relayed by the server via tiktok-live-connector None, but see below

Twitch and Kick never touch the server, which is why they scale for free. Only YouTube and TikTok need a relay, and identical channels share one upstream connection no matter how many viewers are watching.

Self-hosting

Docker

git clone https://github.com/dekrom/BepChat && cd BepChat
cp .env.example .env    # optional; every value has a working default
docker compose up -d

Open http://localhost:8080.

Node

Requires Node.js 22 or newer.

npm install
npm start

To skip TikTok support and run with zero dependencies:

npm install --omit=optional
npm start

Configuration

Every variable is optional. An instance with no configuration at all still serves working overlays for all four platforms — the settings below only affect badge art, capacity and TikTok quota.

Variable Default Purpose
PORT 8080 HTTP port
HOST 0.0.0.0 Bind address
TWITCH_CLIENT_ID Optional app credentials from dev.twitch.tv. Badge art works without them; setting them routes global badges through the official Helix API instead.
TWITCH_CLIENT_SECRET As above
EULER_API_KEY EulerStream key raising the TikTok signing quota
TIKTOK_ENABLED true Set false to disable TikTok relaying entirely
MAX_ROOMS 50 Concurrent upstream relay connections
MAX_CLIENTS 300 Concurrent overlay viewers on the relay
MAX_CLIENTS_PER_IP 5 Per-address relay connection limit
RATE_LIMIT_PER_MIN 30 Requests per minute for the JSON endpoints
ROOM_IDLE_GRACE_SEC 60 How long an unwatched relay stays open, so an OBS refresh does not reconnect upstream
TRUST_PROXY Set 1 when behind a reverse proxy so X-Forwarded-For is honoured
YT_OFFLINE_POLL_SEC 60 How often to check whether a YouTube channel went live
YT_POLL_MAX_MS 2500 Gap between live-chat polls; YouTube asks for 10s, which leaves its messages that far behind Twitch and Kick

Overlay URL parameters

/overlay?twitch=yourname&kick=yourname&size=2&font=6&fade=30&stroke=1

Channels

Parameter Value
twitch Channel login name
kick Channel slug
youtube @handle or UC… channel id
tiktok Username, with or without the @
ytv A specific YouTube video id. Only needed for unlisted broadcasts; public streams are discovered automatically.
kick_room Numeric Kick chatroom id. Only needed if your network blocks Kick's channel API.

Appearance

Parameter Values Default Effect
size 13 2 Text size
font 015 0 Font family — see below
weight 300900 600 Font weight; names render 200 heavier, capped at 900
italic true/false false Italic text
uppercase true/false false Uppercase names
entry slide/fade/pop/grow/none slide Entry animation
fade seconds 0 Remove messages after N seconds; 0 keeps them
stroke 03 0 Text outline width
stroke_color six hex digits 000000 Outline colour
shadow 03 2 Text shadow strength
shadow_color six hex digits 000000 Shadow colour
glow 03 0 Coloured glow layered over the shadow
glow_color six hex digits ffffff Glow colour
spacing 024 4 Pixels between messages
max 1200 40 Messages kept on screen
bg none/dark/light none Message background box
bg_opacity 0100 60 Box opacity when bg is set
direction up/down up Whether new messages appear at the bottom or the top
timestamps true/false false Show HH:MM before each message
chips icon/color/none icon Platform mark: the service's logo, a coloured square, or nothing
badges true/false true Show badges
name_color six hex digits Force one colour for every name. 7TV paints still take precedence.
animate true/false true Legacy alias: false means entry=none

Fonts

id Font id Font
0 System 8 SF Pro (Apple)
1 Arial Black 9 SF Pro Rounded
2 Verdana 10 Segoe UI
3 Comic Sans 11 Tahoma
4 Trebuchet 12 Times
5 Courier 13 Century Gothic
6 Impact 14 Consolas
7 Georgia 15 Palatino

SF Pro ships with macOS and iOS and cannot be redistributed as a webfont, so ids 8 and 9 only render as SF Pro where the font is installed. Everywhere else they fall back to the closest available face. This matters for OBS: the font has to exist on the machine running the browser source, not on the server.

Ids 07 are fixed and will not be renumbered.

Filtering

Parameter Values Default Effect
platforms csv of twitch,kick,youtube,tiktok all Only connect to these platforms
hide_commands true/false false Hide messages starting with !
hide_bots csv of names Hide specific chatters, e.g. nightbot,streamelements

Limitations

These are properties of the platforms, not bugs:

  • YouTube has no public live chat API, so BepChat uses the same internal endpoint the web player does. It can break when YouTube changes their markup.
  • YouTube unlisted streams are invisible to automatic discovery. Use ytv.
  • TikTok requires a third-party service (EulerStream) to sign its WebSocket handshake. The free tier is limited, so on a busy public instance TikTok is best-effort — the overlay reports throttled rather than failing silently. Set EULER_API_KEY for a higher quota.
  • Kick sits behind Cloudflare, which rejects server-side requests based on their TLS fingerprint. BepChat resolves Kick channels in the browser instead, where it works. kick_room is the manual fallback.
  • Twitch badge art and channel emote sets need the channel's numeric id, which the official Helix API only hands out to a registered app. So that a zero-configuration instance still works, BepChat reads the same public data from the endpoint twitch.tv's own web client uses. Setting TWITCH_CLIENT_ID/SECRET switches global badges to Helix.
  • YouTube Super Chats and membership posts are rendered, the former with its amount shown as a badge. Subs, raids, gifts and moderation events on every platform are ignored by design.

Development

npm install
npm test        # node --test, no test framework needed
npm start

There is no build step. The server is plain Node ESM and the frontend is native ES modules, so editing a file and reloading is the whole workflow.

src/           HTTP server, relay rooms, platform sources, upstream API clients
public/        overlay page, builder page, both as native ES modules
shared/        identifier validation used by the server and the browser alike
test/          node:test suites

Credits

Badge and emote artwork belongs to its respective owners: Twitch, Kick, YouTube, TikTok, 7TV, BetterTTV and FrankerFaceZ. Kick's standard badge SVGs are vendored in public/kickbadges/, and each platform's logo is drawn in public/overlay/icons.mjs to identify which chat a message came from. Those marks are the trademarks of their respective owners. BepChat is not affiliated with, endorsed by, or sponsored by any of these services.

Licence

MIT — see LICENSE.

About

Self-hosted, ad-free overlay that merges Twitch + Kick + YouTube live chat into one page for OBS — 7TV/BTTV/FFZ emotes, real badges, zero dependencies.

Resources

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages