만들게 될 것
OBS, Streamlabs 등 브라우저 소스를 지원하는 모든 소프트웨어에서 작동하는 브라우저 기반 방송 오버레이예요. 이런 내용을 보여 줘요:
- 실시간 라이브 채팅 메시지
- 다이아몬드 가치가 표시되는 선물 알림 애니메이션
- 현재 시청자 수
- 사용자 프로필 사진이 함께 나오는 스크롤 채팅 피드
준비물
- Node.js 18+ 설치
- 무료 TikTools API 키 - 여기서 받기
- OBS Studio (또는 브라우저 소스를 지원하는 방송 소프트웨어)
1단계: 프로젝트 설정
mkdir tiktok-overlay && cd tiktok-overlay
npm init -y
npm install @tiktool/live express2단계: 서버 만들기
server.mjs 파일을 만드세요. TikTok에 연결하고 Server-Sent Events(SSE)로 브라우저 오버레이에 이벤트를 전달하는 역할이에요:
import express from 'express'
import { TikTokLive } from '@tiktool/live'
const app = express()
const clients = new Set()
// Serve the overlay HTML
app.get('/', (req, res) => {
res.sendFile('overlay.html', { root: '.' })
})
// SSE endpoint for real-time events
app.get('/events', (req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
Connection: 'keep-alive',
})
clients.add(res)
req.on('close', () => clients.delete(res))
})
function broadcast(event, data) {
for (const client of clients) {
client.write(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`)
}
}
// Connect to TikTok LIVE
const tiktok = new TikTokLive({
uniqueId: process.env.STREAMER || 'tv_asahi_news',
apiKey: process.env.TIKTOOL_API_KEY
})
tiktok.on('chat', (e) => broadcast('chat', {
user: e.nickname, comment: e.comment, avatar: e.profilePictureUrl
}))
tiktok.on('gift', (e) => broadcast('gift', {
user: e.nickname, gift: e.giftName, diamonds: e.diamondCount
}))
tiktok.on('viewer_count', (e) => broadcast('viewers', {
count: e.viewerCount
}))
await tiktok.connect()
app.listen(3333, () => console.log('Overlay: http://localhost:3333'))3단계: 오버레이 HTML 만들기
overlay.html 파일을 만드세요. OBS가 브라우저 소스로 렌더링할 화면이에요:
<!DOCTYPE html>
<html>
<head>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Inter', sans-serif;
background: transparent;
overflow: hidden;
}
.chat-feed {
position: fixed; bottom: 20px; left: 20px;
width: 360px; max-height: 400px;
display: flex; flex-direction: column-reverse;
gap: 6px; overflow: hidden;
}
.chat-msg {
background: rgba(0, 0, 0, 0.6);
border-radius: 8px; padding: 8px 12px;
color: #fff; font-size: 14px;
animation: slideIn 0.3s ease;
}
.chat-msg .user { color: #fff; font-weight: 600; }
.gift-alert {
position: fixed; top: 50%; left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.8);
border: 2px solid var(--border-primary);
border-radius: 16px; padding: 24px 40px;
text-align: center; color: #fff;
animation: popIn 0.5s ease;
display: none;
}
.gift-alert.show { display: block; }
.gift-alert .gift-name { font-size: 28px; font-weight: 800; }
.gift-alert .gift-user { font-size: 16px; color: #94a3b8; }
.gift-alert .gift-diamonds { font-size: 18px; color: #fff; }
.viewer-count {
position: fixed; top: 20px; right: 20px;
background: rgba(0, 0, 0, 0.6);
border-radius: 8px; padding: 8px 16px;
color: #fff; font-size: 14px; font-weight: 600;
}
@keyframes slideIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes popIn {
0% { opacity: 0; transform: translate(-50%, -50%) scale(0.5); }
100% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}
</style>
</head>
<body>
<div class="chat-feed" id="chat"></div>
<div class="gift-alert" id="gift">
<div class="gift-name" id="gift-name"></div>
<div class="gift-user" id="gift-user"></div>
<div class="gift-diamonds" id="gift-diamonds"></div>
</div>
<div class="viewer-count" id="viewers">👁 0</div>
<script>
const chat = document.getElementById('chat')
const gift = document.getElementById('gift')
const src = new EventSource('/events')
src.addEventListener('chat', (e) => {
const d = JSON.parse(e.data)
const el = document.createElement('div')
el.className = 'chat-msg'
el.innerHTML = `<span class="user">${d.user}</span> ${d.comment}`
chat.prepend(el)
if (chat.children.length > 50) chat.lastChild.remove()
})
src.addEventListener('gift', (e) => {
const d = JSON.parse(e.data)
document.getElementById('gift-name').textContent = `🎁 ${d.gift}`
document.getElementById('gift-user').textContent = `from ${d.user}`
document.getElementById('gift-diamonds').textContent = `${d.diamonds} 💎`
gift.classList.add('show')
setTimeout(() => gift.classList.remove('show'), 4000)
})
src.addEventListener('viewers', (e) => {
const d = JSON.parse(e.data)
document.getElementById('viewers').textContent = `👁 ${d.count.toLocaleString()}`
})
</script>
</body>
</html>4단계: 실행 & OBS에 추가
TIKTOOL_API_KEY=your_key STREAMER=target_username node server.mjs그다음 OBS에서:
- 새 브라우저 소스 추가
- URL 칸에
http://localhost:3333입력 - 너비는 1920, 높이는 1080에 맞추기
- "보이지 않을 때 소스 종료" 체크
커스터마이징 아이디어
- 커스텀 테마 - 색상, 글꼴, 레이아웃을 내 방송 브랜드에 맞게 바꾸기
- 효과음 - Web Audio API로 선물이 들어올 때 소리 재생하기
- GIF 애니메이션 - 특정 선물에 맞춰 GIF 애니메이션 보여 주기
- 목표 바 - 후원/선물 목표 진행 바 추가하기
- 톱 후원자 시상대 - 톱 후원자 3명을 실시간으로 보여 주기