At a glance

  • What: Undertow Pace keeps Twitch chat readable in a flood by moving Twitch's own slow-mode dial in small steps, and says why in one sentence per step. (The video and the code use the short form "Undertow".)
  • Not a content filter: AutoMod judges what is said; Undertow Pace handles how fast. It runs alongside AutoMod and Shield Mode, and the streamer's moderators keep every decision about people.
  • Proof on Twitch: the video shows Twitch's own "Slow Mode" banner and the settings panel changing, then the channel restored to the streamer's baseline. The flood is a labeled load test (8 team accounts, own channel): about 2.3 to 0.9 msgs/s, scaled test, 8 labeled accounts.
  • Codex: wrote all code in one thread (01a17fa0-44a2-7081-b2bc-68084379eebb) and writes the post-storm report.

Project Story

Inspiration

A raid or a gift-sub wave lands at night, no moderator is online, and the streamer cannot finish a sentence. Chat tools usually make chat do more. We wanted one that makes a flooded chat calmer, using only the levers Twitch already gives a broadcaster, and that is gentle enough that a streamer would leave it on.

Who benefits when chat slows down: the streamer gets a chat they can answer, because the number that matters is read messages, not sent messages; viewers get a fair turn, because slow mode is per person, so the fast typist stops dominating and the newcomer who arrived with the raid can be greeted; and the volunteer moderator stops being the dial operator at midnight and goes back to judging people and content, which is what AutoMod and a human are for. Twitch itself treats a flood as an emergency: Shield Mode exists for it, and small-streamer guides recommend slow mode right after a raid and turning it off later. We did not run streamer interviews, so the pain is argued from those sources, not measured.

What it does

What a viewer sees: Twitch's own "Slow Mode" bar appears in chat, one polite sentence explains it, and a few minutes later the bar disappears and chat is back to normal.

Undertow listens to Twitch chat over EventSub and is, to our knowledge, the first closed-loop controller for chat pace on Twitch: a setpoint, bounded steps over Twitch's own levers, every move explained in one sentence.

  • The loop. The real profile measures messages per second over a 10 s window against a setpoint of 6 msgs/s. After the pace stays above the setpoint for a 20 s dwell, it steps up one rung: slow mode 3 s, slow mode 10 s, slow mode 30 s, followers-only 10 min, emote-only. It steps back down one rung at a time after the pace stays below half the setpoint for 30 s. Between those levels it holds.
  • Twitch does the enforcing. Every change is a real Twitch chat setting (Update Chat Settings), so Twitch's own banner appears in chat. It is not a bot deleting messages.
  • One kind sentence per step, threaded under the latest message from someone else, for example: "Slow mode 3 s: chat hit 3.5 msgs/s. Back to normal when it drops under 0.75 msgs/s for 30 s." On the way down: "Easing to slow mode 3 s. Back to normal in 15 s if chat stays under 0.75 msgs/s." At the end: "Chat is back to normal. Settings restored."
  • The streamer always wins. A manual change by the streamer makes Undertow yield and leave that setting alone. It saves the baseline before touching anything and restores only the settings it still owns, also after a crash (npm run restore).
  • A needle overlay (OBS browser source) shows pace, setpoint and the current step with one number per line.
  • After the storm, Codex writes a report. npm run report gives a copy of the storm log to codex exec in a read-only scratch directory. Codex proposes thresholds, a replay simulator scores the proposal, and the config changes only if the human runs --accept after a passing score on the same log. Modeled numbers carry the word "modeled"; recorded ones are labeled "recorded".

Prior art: before building we looked for a pace or slow-mode controller among the 67 GitHub repositories created since the Submission Period opened (scan 2026-10-29) and found none; the Devpost gallery was not yet public, so we cannot speak for other entries.

How we built it

Codex wrote all the code in this repository, in one thread (session 01a17fa0-44a2-7081-b2bc-68084379eebb). The human directed each numbered request, reviewed every change, ran it against Twitch and committed. AGENTS.md holds the working rules Codex followed, and docs/BUILD_EVIDENCE.md lists every commit with the request it came from where that mapping is certain. The controller is a pure function (src/controller) with no I/O, so Codex could test it without Twitch; around it sit the EventSub client with reconnect, the settings actuator, the reply sender, the run loop with baseline restore, a replay simulator, the overlay and a load-test sender. Built in 23 turns in one Codex thread (counted from the session file on 2026-10-29). Tests: 144 passing (npm test), plus npm run check.

Codex is not in the live loop. It builds the product and, after a storm, reads the log and proposes thresholds that the simulator must accept.

Challenges we ran into

  • A strict rule stalled the ladder. In the first live rehearsals the ladder stopped at slow mode 3 s, because one quiet second reset the dwell timer. We added a tolerant step-up rule (at least 80 percent of the one-second readings in the dwell above the setpoint) and kept step-down strict. Later rehearsals climbed two rungs.
  • Our own prediction was wrong. We predicted what eight accounts would send under slow mode 3 s and were off; the measured rate was lower, so we scaled the load-test profile (setpoint 1.5 msgs/s) instead of arguing with the arithmetic.
  • Ownership of settings. An early restore undid a manual change by the streamer, and a full-object echo from Twitch made the controller lose track of what it had changed. Both are fixed and tested (manual change wins, restore touches only what Undertow still owns).
  • Twitch field names. EventSub sends slow_mode_wait_time_seconds and follower_mode_duration_minutes, not the names the first code expected. We found it by checking the documentation and recorded payloads.
  • A reply threaded onto our own reply. Fixed: when the latest message is Undertow's own, the notice goes out unthreaded.

What did not work, and what we rejected

  • An Extension: review takes 5 to 14 days and judges cannot install unreviewed Extensions, so we built on chat, EventSub and the Twitch API.
  • A first live flood at 0.6 msgs/s per account reached only slow mode 3 s. The fix was the scaled profile above, not a hotter flood.
  • Putting a model in the live decision loop. The controller must be predictable and testable, so the decision is deterministic and Codex works before and after the storm.

Accomplishments that we're proud of

  • Six consecutive live runs on a real Twitch channel gave the same arc: slow mode 3 s, then 10 s, a fall in pace, a gentle step back down, then the channel restored exactly to its baseline.
  • The restore is the feature a streamer would judge first, and it holds: a manual change wins, a restart refuses to start over an unrestored baseline, and a failed restore says so loudly.
  • The honesty rules in the code: the simulator's response model is labeled synthetic, a real-channel run never prints a modeled number, and the load test is labeled on screen.

What the load test showed (and does not show)

The flood in the video is a labeled load test: eight plain accounts owned by the team, on the team's own channel, each sending neutral lines with a visible [load test] prefix, against a scaled profile (setpoint 1.5 msgs/s instead of 6). The measured arc is about 2.3 to 0.9 msgs/s, scaled test, 8 labeled accounts; the pace peaked near 3.5 to 3.7 msgs/s before the first step, and 2.3 msgs/s is the pace when slow mode 10 s engaged. Eight accounts under slow mode 10 s can send at most about 0.8 msgs/s (one message per 10 s each), so the low end of that range is the ceiling of this setup. It shows that Undertow moves Twitch's own settings and that the pace follows. It does not show that the thresholds are tuned for a real channel. The real profile (setpoint 6 msgs/s) has not been run on a real channel with real viewers.

What we learned

Gentle and reversible matters more than clever. The Twitch limits the code relies on (slow mode 3 to 120 s, followers-only 0 to 129600 min, scope moderator:manage:chat_settings, EventSub channel.chat_settings.update) come from Twitch's own API reference, and we wrote them into tests. Two assumptions are still open: whether viewers accept slow mode 10 s for a couple of minutes (a streamer guide advises 15 to 30 s, https://www.streamscheme.com/?p=8714), and whether a visible needle makes a streamer less likely to override by hand. Step 2 of the ladder is a value to tune in the simulator, not a fact.

What's next

A pilot on a small real channel with the real profile, tuning the slow-mode values from real chat, a settings screen instead of editing undertow.config.json, and the report's one-sentence cause shown on the overlay at the end of a storm. Two safety hooks on the same levers: pre-step when Twitch's Shield Mode turns on (EventSub reports the activation), and treat a surge of AutoMod-held messages like a surge in pace. Content judgment stays with AutoMod and the moderators.

Links

Built With

Share this project:

Updates

Submission history