Inspiration
Every Twitch viewer watches the stream late, and each one by a different amount. In my tests from Lagos, one phone was ~9 seconds behind and another ~22 seconds. Any "call it" game that locks for everyone at one moment is unfair: fast viewers can call after they've already seen the result, and slow viewers get rejected for calling honestly.
What it does
CalledIt is a fair clock for Twitch chat.
- SYNC: a word flashes on stream. Each viewer types it back, and CalledIt measures that viewer's personal delay. No extension, no install — just chat.
- OPEN: the streamer opens a round (land / miss). Chat calls with !land or !miss.
- JUDGE: the streamer marks when it happened. Every call is judged against that viewer's own delay: a call only counts if it arrives before that viewer could have seen the result.
Every verdict shows its numbers (call time, cutoff, delay), so nobody argues. The bot posts one summary per round. If the Twitch connection drops during a round, the round is voided, not guessed.
How we built it
I built CalledIt with OpenAI Codex in one main thread, phase by phase from a PRD:
- a pure judging engine (zero dependencies) with a 10,000-round property test for the fairness rule,
- the Twitch integration: OAuth, EventSub WebSocket chat, gap detection, rate-limit-aware bot summary,
- the OBS overlay, streamer dashboard and the /judge replay. When live tests on my real channel failed, I pasted the server logs back to Codex and it found and fixed the real causes (a 15-second sync window was too short for real Twitch delays; the overlay wasn't following the live round). 74 tests pass.
Challenges we ran into
- I'm not a streamer. I tested on my own channel with OBS, extra accounts and a friend's phone.
- Real delays were longer than planned. My 15 s sync window rejected every reply, because the stream from Lagos ran 15+ s behind. I raised the live window to 45 s.
- Bugs only showed up live: the sync word didn't appear on stream, the dashboard dropped when I switched tabs, and one account chatted logged-out. Server logs pasted into Codex helped fix each one.
- Testing fairness is tricky. Pressing both phones at once, or clicking "it happened" late, gave confusing results. CalledIt was right, but I had to learn how to run a clean test.
Accomplishments that we're proud of
- It works live on real Twitch. In a real round, the fast viewer who already saw the shot was blocked (LATE_SAW_IT) and the slow viewer who called honestly was accepted.
- Fair with just chat. No extension, no install, no affiliate status needed.
- Every verdict shows its numbers, so chat can see why a call counted or didn't.
- 74 passing tests, including a 10,000-round fairness test.
- Built solo with Codex by someone who had never streamed before.
What we learned
- Twitch delay is bigger and messier than you'd think. 9 s on one phone, 22 s on another, for the same stream.
- Live testing finds what unit tests miss. Most real bugs only appeared on my actual channel.
- Good logs make AI coding much better. Pasting real server logs into Codex turned guesses into quick, exact fixes.
- Plan the demo early. Designing the money shot first kept the build focused.
What's next for CalledIt
Overlay-triggered events for an exact event time, a season leaderboard, and Shared Chat support for collab streams.
Note: the free server may take ~1 minute to wake on first visit.
Built with
Node.js, Express, ws, React, Vite, Twitch EventSub WebSocket, Twitch Helix API, OBS browser source, Render, OpenAI Codex.
Honest limits
- A viewer's measured delay includes their reaction time and chat delivery.
- The streamer's button press adds their own reaction time.
- A viewer could reply slowly on purpose during sync to get a later cutoff; using the lowest of several samples and a cap reduces this but doesn't remove it.
- Lost EventSub events are not replayed by Twitch, so that round is voided.
Built With
- codex
- express.js
- node.js
- obs-browser-source
- openai
- react
- render
- twitch-eventsub-websocket
- twitch-helix-api
- vite
- ws
Log in or sign up for Devpost to join the conversation.