-
-
Imagine following a raid into a stream you have never seen. Chat is spamming MOON JUMP, and nobody has time to explain why.
-
Previously answers before anyone asks: the raid gets a short Previously on, cut from the stream’s own clips with a line for each.
-
It plays like the recap before a TV episode: a welcome, the moments that matter, then what is happening right now.
-
Whoever arrives later types !previously, and the bot replies with the same story in chat, so the streamer never has to stop.
-
The link opens the story on a phone without an account, and a viewer can stop reading at any point to avoid spoilers.
-
Streamers pick the recap language, so a Spanish or Japanese channel gets its Previously on in its own words.
-
The streamer keeps the last word from a dock in OBS: rewrite a chapter, pause listening or wipe the last two minutes.
-
One correction is enough. The recap page, the chat answer and the next raid scene all switch to the streamer’s wording.
-
The model only proposes. Code checks every sentence against what was said or typed, then waits 75 seconds before showing it.
-
If something should never have been said, Forget removes it from every chapter, clip, translation and queued scene.
-
Codex built it in one session with parallel workers, and the chat pipeline kept all 1,000 test messages sent at 20 a second.
-
Try the example in your browser right now with no keys, then run Previously on your own channel with your Twitch app and OpenAI key.
Inspiration
We started with the question that keeps interrupting live streams: "What did I miss?" It is hardest to answer during a raid, when a whole audience arrives at once without any of the context the regular viewers share.
What it does
Previously keeps a short, editable story of a Twitch stream while it is live. It gathers the streamer's speech, chat and stream events, checks each proposed statement against its sources, and waits 75 seconds before making it public. A late viewer can read what is happening, open the source behind a chapter, or type !previously in chat. When a raid arrives, the same published story plays as a short OBS scene with the stream's real clips, the chat moment that started a running joke, and what the streamer is doing now. The streamer picks the recap language, English by default or any other, and every output follows it.
The streamer can correct a chapter, pin useful context, save a moment, pause listening or forget the last two minutes. Forget follows the affected sources through chapters, translations, channel terms, media and replay exports, and a model reply that arrives later cannot restore them. Older history stays available without sending the whole stream to the model on every request.
The example recording shows this without any account. Foxlight Dev builds a tiny cave platformer, a spring pad launches the fox through the ceiling, chat names it the moon jump, and a raid from pixelmoth arrives at the end. The replay runs the recorded events and model outcomes through the same story rules as the live app. Applying the supplied correction changes the chapter and the next raid scene, while your own wording stays held because the recording has no safety check for it.
How we built it
We built the product in one Codex session, from research and architecture through implementation, integration and review. Parallel Codex workers each owned one part: the runtime, storage, Twitch adapters and the interface. The main session joined those parts and checked the resulting user journeys. Near the end, Claude agents did the final visual pass, covering the new mark, the rebuilt raid scene and pages, the Foxlight example and the README.
A local HTTPS process runs beside OBS. Microphone chunks from the browser and Twitch EventSub events reach a TypeScript story engine backed by SQLite. OpenAI adapters handle transcription, structured proposals, grounding and privacy checks and optional narration. Only published, permitted material crosses the signed boundary to Vercel Functions and a private Blob store, and the public page and replay check access again on every read instead of trusting a permanent asset URL.
We used deterministic fixtures for difficult races and the official Twitch CLI for real mock WebSocket deliveries. Deployed publication tests exercised the actual cloud boundary. The live path has been built against the current official Twitch and OpenAI contracts, but it has not been run on a real broadcast with a real OpenAI key. Judges who want to see it live can run it with their own Twitch application and OpenAI key.
Challenges we ran into
Keeping the story useful without inventing connections was harder than producing a summary. Early designs treated a coding stream's goal, problem and fix as a shape every stream follows. That was wrong for most streams, so the model now keeps only the roles the current source supports and leaves uncertain channel terms unpublished.
Privacy had to survive timing failures. A full chat queue at first dropped a deletion that had already arrived. We reproduced it, added durable handling for privacy controls and tested recovery after a process crash. A slow model result that returns after Forget also has to fail to bring the removed content back.
Long sessions exposed expensive repeated state recovery. Bounded batching improved the real path from EventSub to SQLite while keeping durable receipts and explicit gaps under overload. Older chapters now load through real cursors instead of a button that only revealed more of a list that was already loaded.
Accomplishments that we're proud of
The raid scene is built only from what was already published: the chapters, their clips, the chat moment with its real message count and the running joke chat named. Nothing is generated when the raid arrives, so the scene starts at once, and a source that is deleted or forgotten drops out of the next scene.
The example replay lets anyone inspect sources, apply a checked correction and watch the raid scene with real gameplay clips, all without an API key. Recording management keeps private capture, content review, offline export, publication and withdrawal as separate steps. An offline ZIP includes the selected reviewed media, and withdrawing a published recording leaves its original source material in place.
Chat intake held up under load in local tests: 1,000 messages at 20 per second were committed with no gap and a receive to commit p95 of 49.84 ms. The story core processed 577,200 messages across eight virtual hours within its limits. A separate run of the full engine with SQLite kept 280,780 messages over 3.80 virtual hours with every freshness check in its last saved sample met before we stopped it early, so eight-hour stability of the full engine is still unverified.
Cloud tests checked that stale publications are rejected and that old replay asset URLs stop working after revocation. The official Twitch CLI checks covered raid delivery, reconnect, keepalive loss and process restart. None of these results is presented as a completed live broadcast.
What we learned
The hard part of a catch-up tool is deciding when a statement is safe to reuse. A chapter, a translation, a clip and a raid scene can all depend on one source that later disappears. Keeping those dependencies explicit made corrections and forgetting part of the mechanism.
We also learned to look at the product beyond passing tests. We threw away an early interface because it looked generic. Pixel reviews then caught cramped mobile controls, a hard-to-read scene and an awkward recording review layout. Functional tests and visual review answered different questions.
What's next
Run the first live stream with a real OpenAI key, check microphone and model quality in several languages, and record a real raid in OBS. Add a reviewed recording of a real stream next to the authored example. After that, the work is to learn from streamers using it on different kinds of streams while keeping sources visible and the privacy controls easy to reach.
Built With
- express.js
- ffmpeg
- mediadevices-api
- node.js
- obs-studio
- openai-api
- openai-codex
- playwright
- react
- sqlite
- twitch-cli
- twitch-eventsub
- twitch-helix
- typescript
- vercel-blob
- vercel-functions
- vite
- vitest
- web-audio-api
Log in or sign up for Devpost to join the conversation.