For small and mid-size Twitch streamers who lose the room each time a new viewer asks "what did I miss?"
How this entry answers the judging criteria
- Innovation/Idea: Chat bots answer fixed commands. Catch-up Clerk reads the live chat and stream events and writes a new recap for each late viewer.
- Potential Impact: The streamer keeps talking. The new viewer gets the story in one reply. Mods stop typing the same answer.
- Technological implementation: EventSub WebSocket, Helix threaded replies, a digest step, Gemini with a local fallback, and 52 tests. One OpenAI Codex session wrote the code.
- Design: One command for viewers, one OBS card on stream, and one side panel for the streamer. You can try the hosted demo with no Twitch account.
Inspiration
Every stream has the same moment. Someone joins in the middle of the stream. That viewer asks "what did I miss?" The streamer can repeat the story and lose the room for a minute. The streamer can also ignore the question, and then the new viewer stays lost. Mods answer the question by hand, again and again. Chat and the stream events already hold the answer. Nobody has time to read them back.
What it does
A viewer types !catchup in chat. Catch-up Clerk reads the last 20 minutes of chat and Twitch events. The events include follows, raids, subs, gift subs, cheers, title and category changes, and stream online and offline. Clerk answers in two places:
- A threaded reply in Twitch chat, under 450 characters, to that viewer.
- A 12-second welcome card on stream through an OBS browser source: "Welcome @viewer, here's what you missed", three bullets and the current topic.
The streamer gets a side panel that stays current. The panel shows the current topic and key moments with timestamps. It also shows open questions, running jokes, recent events and the viewers who asked to catch up. At the bottom, it gives one line that the streamer can read aloud to newcomers.
Each viewer has a 60-second cooldown. The channel has a 15-second global cooldown. These limits stop the command from flooding chat. The broadcaster and mods can type !recap to refresh the panel at once.
How we built it
I built the whole project in one OpenAI Codex session (01a1824c-c5ae-7343-821f-3096db1449b4). Codex wrote the code, tests, demo fixture and README. I wrote the specs, tested each round against a real Twitch channel and sent back what I saw.
- Node 26 with native
fetch,WebSocketandhttp. No runtime dependencies and no UI framework. - Twitch: OAuth authorization code flow, an EventSub WebSocket session with 10 subscriptions, and Helix
POST /chat/messageswithreply_parent_message_idfor threaded replies. - Summaries: a 20-minute ring buffer feeds a digest step. The digest step collapses spam and emote floods, keeps every event and samples chat evenly. The digest goes to Gemini 3.1 Flash-Lite through its OpenAI-compatible endpoint and comes back as structured JSON. A local summarizer answers when there is no key, the JSON is bad, or the call takes over 8 seconds.
- Panel and overlay are plain HTML, CSS and JavaScript fed by Server-Sent Events.
- A demo mode replays a seeded, recorded 25-minute session of a fictional channel. You can try the whole flow without going live. The same core runs in the browser for the hosted demo on a Cloudflare Worker. The hosted demo uses the local summarizer.
- 52 tests run on
node --testwith fake Twitch fetch and WebSocket servers.
Challenges we ran into
- Chat volume. A busy channel produces thousands of lines in 20 minutes. Most of the lines are emotes. A model call with all of that is slow and mostly noise. The digest step does the heavy lifting before any model call.
- EventSub WebSocket lifecycle. Server-directed reconnects have to keep subscriptions. Unexpected drops need a fresh session and new subscriptions. Tests cover both paths.
- Reply loops. When the bot account is also the broadcaster, its own replies come back through
channel.chat.message. Clerk tracks the message IDs that it sent and skips them. - Shared Chat. During a Shared Chat session, a user token cannot limit a reply to one channel. Clerk checks for an active session before each send. If a session is active, Clerk holds the reply.
- Scale limits. One process serves one channel, and the 20-minute buffer stays in memory. A large channel with many
!catchuprequests will hit the global cooldown first.
Accomplishments that we're proud of
- It works on a real channel. On 2026-10-28, all 10 EventSub subscriptions came back as subscribed on a live channel.
!catchupproduced a threaded reply in Twitch chat. - The command never goes silent. Every failure path falls back to a local summary.
- Anyone can try it in a browser with no Twitch account. Anyone can also run it locally with
npm run demo.
What we learned
- EventSub over WebSocket is enough for a single-channel tool. A public webhook endpoint is not necessary.
- Most of the summary quality comes from what you keep before the model call, not from the model.
What's next for Catch-up Clerk
- A Twitch extension panel. Viewers can then open the recap without typing in chat.
- Separate bot and broadcaster authorizations. Replies can then come from a dedicated bot account.
- Clip and VOD timestamps in the key moments list.
- Localized replies based on the viewer's chat language.
Built With
- cloudflare-workers
- eventsub
- gemini
- javascript
- node.js
- obs
- openai-codex
- server-sent-events
- twitch
- twitch-chat
Log in or sign up for Devpost to join the conversation.