Projects
The live audience is the controller

TikBox

Four games driven by a TikTok Live stream. Chat messages, likes and gifts are the inputs, the viewers are the players, and the whole thing renders into the 9:16 frame the stream actually captures.

Role
Solo — architecture, game engine, interface, content
Period
2025 — 2026
Type
Lab
Status
shipped
3,610
Words with definitions
1,200
Trivia questions
27
Letters per rosco
13.6k
Lines of TypeScript
01

Showcase

1 video
Loading showcase
31sTikBox — The 9:16 overlay itself — connecting a stream, then chat, the race and the rosco. Rendered live with Remotion, not a video file.
02

Overview

A live stream is already thousands of people sending input every second — it is just usually treated as chat. TikBox turns that into a controller: comments, likes and gifts are the inputs, viewers are registered as players the moment they type, and the game renders into a 9:16 frame captured straight into TikTok LIVE Studio. Nobody installs anything or leaves TikTok.

Built for my own streams and finished: four games, a word bank written for the format rather than scraped into it, and a simulated event source so the whole thing can be developed at three in the morning with no audience.

The aspect ratio is the constraint that shapes everything else. Nine by sixteen is not a preview size here, it is the canvas — what fits on screen is decided before a component gets written.

The demo runs the real client with the event generator moved into the browser, so the four games play out against a simulated audience with no server behind them. Open it and the chat, gifts and likes are being produced in your own tab.

03

Architecture

1 diagrams
01

From chat message to screen

Event sourcesMOCKED=trueTikTok LIVEchat, gifts, likesMock generatorrates, probabilitiesLive clientnormaliseWebSocketshared protocolGame storestate, actions,timers9:16 visorcaptured inLIVE Studio

Scroll to pan

One path, and a second source that feeds the same normaliser. The mock generator exists because a chat game cannot be developed live — it needs an audience, and an audience does not show up on demand.
04

Screens

10 captures
  • TikBox account setup with the live stream connected
    Stream connection — the handle is validated before any game runs
  • Game lobby listing Chat, Race, Trivia and Pasapalabra with live player counts
    Lobby — four games, live player counts per game
  • Live chat stream with filters for chats, gifts and likes
    Chat stream — every event typed and filterable
  • Race game countdown with players lined up on the starting grid
    Race — viewers auto-join the grid, then the countdown starts
  • Race complete overlay announcing the first place winner with confetti
    Race complete — winner overlay, then the next race
  • Race settings dialog with player limits, race time, win position and boost values
    Per-game settings — the streamer tunes the rules live
  • Trivia question with difficulty badge, countdown and answered count
    Trivia — question on the clock, answers arrive as chat
  • Trivia answer reveal with correct and wrong counts and a top three leaderboard
    Reveal — scoring resolved, top three updated
  • Pasapalabra rosco with letters marked correct, passed and current
    Pasapalabra — the rosco tracks 27 letters in real time
  • Mock control panel with sliders for chat, gift and like event rates
    Mock control — the whole stream simulated, rate by rate
05

Modules

5 units
01

Stream as input

TikTok Live events — chat, gifts, likes, follows, viewer count — translated into typed game events before anything else sees them, so a change in the underlying library never reaches a game.

02

Four surfaces, one shell

Chat, Race, Trivia and Pasapalabra share the same shell: connection state, player registry, config dialog, winner and timeout overlays. A new game is the rules, not the plumbing.

  • Pasapalabra — the 27-letter rosco, themed generation, three scoring modes
  • Trivia — multiple choice on the clock, points for speed and streaks
  • Race — every message, like and gift pushes a car toward the line
  • Chat — the stream on screen, filterable by event type
03

State split three ways

Every game store is divided into state, actions and timers. Timers are the part that rots first in a real-time game, so they live in their own file and the rest of the store stays readable.

04

Settings declared as data

Each game describes its options as an array of typed fields with ranges and validation, and the settings dialog draws itself from that. Adding a setting is adding an object, not editing a form.

05

The simulated stream

A generator that produces chat, gifts, likes, shares and follows at configurable rates and probabilities, controllable over HTTP while the app runs — including a pause, which turns out to be the most used debugging tool in the project.

06

Engineering

What was hard
01

A word game is only as good as its dictionary

Pasapalabra needed 3,610 Spanish words, one set per letter including the Ñ, and a flat list would have been useless. Every entry carries its own definition, difficulty, topics, length and — the detail that actually changes the game — a list of alternative answers. Without those, half the room types something perfectly reasonable and the game tells them they are wrong.

02

Developing a chat game with no chat

A game whose only input is a live audience cannot be built in front of a live audience. The mock service generates every TikTok event type at tunable intervals and probabilities over a pool of fake viewers, and exposes them over HTTP so a specific gift or message can be fired by hand. Freezing the stream mid-round to inspect a state that would otherwise keep moving is what makes the games debuggable at all.

03

Timers are where real-time games rot

Countdowns, per-question limits and automatic round transitions all run against events that arrive whenever they want. Isolating them in their own module per game — separate from state and from actions — is what keeps the rest of each store something you can read without holding a clock in your head.

04

A question that never finished typing

The typewriter effect rescheduled a timeout per character, so with live events arriving the timeout was cleared and re-armed faster than it could fire: roughly 500ms per character against the 30ms configured, which meant a 40-character question needed 20 seconds and the round moved on before it was readable. One interval with the index held outside React state brought it back to 28ms. It only appears under event load, which is exactly why it survived so long.

07

Stack

10 entries
  • 01Next.js 15
  • 02React 19
  • 03TypeScript
  • 04Zustand
  • 05Framer Motion
  • 06Node.js
  • 07Express
  • 08WebSockets
  • 09TikTok Live
  • 10Docker