← back

StudyTomato

2025 · React, TypeScript, FastAPI, WebSockets, Docker

Everyone online studies for 25 minutes, then plays Minesweeper together. The timer is derived from UTC, so there's no shared state to sync. FastAPI and WebSockets.

Everyone on StudyTomato is on the same clock. The timer isn’t stored per user: every client derives the phase from UTC (25 minutes of study, 5 of Minesweeper, a 30-minute cycle), so there’s no state to sync and a fresh page load is always correct. The server runs the shared Minesweeper game and pushes it over WebSockets. Visitors can play as a guest without signing up, and an admin can override the cycle live. A mine costs points and ten seconds instead of the rest of your break, and a cleared board is replaced by a new one, so all five minutes stay playable.

A decision I made on purpose: I considered Redis to share timer state across FastAPI workers, but a single worker is enough at this traffic, and the UTC-derived timer removes most of the shared state anyway. Redis is the next step if it ever needs more than one worker.