Chat-Plays
Why you’d want this
Section titled “Why you’d want this”Channel-point Rewards are one viewer paying for one effect. Chat-Plays flips
that: your whole chat drives the game together, for free. Viewers type a
button name (up, a, start, …), the bridge tallies the votes over a window,
and the winning button is pressed. No redemption, no cost, just volume.
High-spam is the point. Reach for it when the fun is collective input rather
than individual purchases. It works with any chat source.
Opting in
Section titled “Opting in”Chat-Plays is strictly opt-in via the [input_vote] table in pack.toml.
A Pack that omits it has no Chat-Plays at all: no state keys, no Dashboard
control, no input delivery.
[input_vote]window_ms = 500 # vote-tally windowpress_duration_ms = 250 # momentary-press length
[input_vote.vocabulary]a = 0b = 1start = 6up = 8down = 9left = 10right = 11vocabulary(required, non-empty) maps a chat token to a pad bit index (0..=31). The token is what a viewer types; the bit index is the position set in the pad mask when that token wins. What a bit means physically is your Adapter’s business: the platform never learns it. Each token must be a lowercase identifier ([a-z][a-z0-9_]*); duplicate bit indices are allowed (two tokens can map to the same physical button).window_ms(> 0): votes arriving within one window are tallied together; at window close the winner is selected and pressed. A shorter window feels more responsive to chat but has fewer votes to aggregate, so a handful of fast typers can decide it; a longer window is more democratic but laggier. 500 ms is a good starting point: short enough to feel live, long enough to gather a real crowd of votes.press_duration_ms(> 0): a winning vote is a momentary press. The winning bit is latched into the pad mask, then auto-released to0after this many milliseconds, so a tap never becomes a permanent hold. Choose a duration long enough for your game’s per-frame input poll to register (250 ms ≈ 15 frames at 60 fps).
How a vote works
Section titled “How a vote works”- A viewer types a chat message. The whole trimmed, case-folded text must
equal a vocabulary token:
"UP"counts asup;"go up"counts as nothing. Every matching line counts; there is no per-viewer dedup (spam control is the broadcaster’s lever: slow mode, mods). - The cloud relays chat lines pack-blind: it never sees your vocabulary and never tallies. Matching and tallying happen on the bridge, generically.
- At window close, one button is selected (see modes below) and its bit is delivered to the Adapter.
Votes are only counted while Chat-Plays is active (chat_plays_active, below).
Selection modes
Section titled “Selection modes”The broadcaster picks the selection mode via the platform chat_plays_mode
setting (not Pack data; your Pack declares vocabulary and timing only):
- democracy (default): the single most-voted button wins; ties break randomly among the top-voted tokens.
- anarchy: one random cast vote wins, weighted by vote count.
Both modes press exactly one button per window.
Turning it on and off
Section titled “Turning it on and off”The effective on/off, chat_plays_active, has two sources, combined by the
platform:
chat_plays_enabled: a broadcaster toggle (manual on/off in the Dashboard).- A viewer Reward that opens a timed window. Declare an Event whose params
carry
chat_plays_window_msand bind it to a Reward; redeeming it opens Chat-Plays for that long, independent of the broadcaster toggle:
[events.chat_plays_window]summary = "Open a timed Chat-Plays window."title = "Chat Plays: open a window"queue = "default"[events.chat_plays_window.params_schema]type = "object"required = ["chat_plays_window_ms"]additionalProperties = false[events.chat_plays_window.params_schema.properties.chat_plays_window_ms]type = "integer"minimum = 1000maximum = 600000description = "How long (ms) the Chat-Plays window stays open."chat_plays_active = chat_plays_enabled OR window-open.
What the Adapter receives
Section titled “What the Adapter receives”The bridge delivers two latched values over the same config frame channel as
Pack Config (replayed on every Adapter connect, pushed on
change):
chat_plays_active(boolean): whether Chat-Plays is currently live. Use it to gate/lock out local pad input if desired.chat_plays_pad_mask(integer): the current pad bit mask. OR it into your game’s joypad input each frame while active. Delivery is vote-cadence latched; there is no per-frame push, so your game loop samples the latched mask.
See also
Section titled “See also”- Manifest reference:
[input_vote]in the TOML tree. - Pack Config: the latched delivery channel these values ride.
- Events: declaring the window-opening Event.