Web app for Sport Media Focus d.o.o.
Olimpijski Kviz — Milano-Cortina 2026
Real-time quiz platform for 25,000 concurrent viewers during live Olympic broadcasts
Overview
A real-time interactive quiz platform built for the Milano-Cortina 2026 Winter Olympics. Every evening during the Games (Feb 6–21), TV Slovenia 2 broadcast a QR code and up to 25,000 viewers joined a live quiz on their phones — answering 16 timed questions about Olympic history, Slovenian athletes, and winter sports trivia, competing for a trip to the LA 2028 Summer Olympics.
16 daily quizzes. 15 seconds per question. Time-based scoring. Live leaderboards. All synchronized without WebSockets.
The challenge
- Keep 25,000 concurrent mobile users in sync during a live TV quiz with sub-second accuracy
- Handle answer submissions idempotently under unreliable mobile connections (double-taps, retries, network drops)
- Deliver a smooth, real-time experience using server-side rendering only — no WebSockets, no client-side JS framework
- Integrate sponsor video ads mid-quiz without breaking the flow
- Go from zero to production in ~4 weeks
The solution
A poll-based state synchronization architecture: HTMX morph-swaps poll the server every 1–2 seconds, and a Redis-cached quiz state machine drives phase transitions automatically via BullMQ workers.
State machine with 6 phases manages each quiz:
LOBBY → QUESTION → SPONSOR_EXPOSITION → WAITING → STANDINGS → COMPLETED
Caching layer with 95%+ Redis hit rate eliminates database pressure — leaderboard queries reduced from ~25,000 to 1 per question round (25,000× improvement). TTL jitter prevents cache stampedes.
Idempotent submissions via Prisma transactions prevent double-counting. Distributed Redis locks prevent race conditions during phase transitions.
Key metrics
| Metric | Value |
|---|---|
| Concurrent users | 25,000 |
| Throughput | 2,959 req/sec sustained |
| Median latency | 108 ms |
| Requests under load test | 1,066,605 (zero failures) |
| HTTP success rate | 100% |
| Cache hit rate | 95%+ |
| N+1 query elimination | 25,000× reduction |
| Quizzes delivered | 16 (daily, Feb 6–21) |
| Questions per quiz | 16 (4 choices, 15 sec timer) |

Codebase
| TypeScript files | 141 (43,044 LOC) |
| JavaScript (k6 load tests) | 13 (2,132 LOC) |
| Total lines | 58,226 |
| Test files | 34 (19,000+ LOC) |
| Prisma models | 13 |
| Database indexes | 30+ strategic |
| k6 load test scenarios | 13 |

Architecture
- Fastify v5 + TypeScript — SSR with fluent-html + HTMX + Tailwind
- PostgreSQL + Prisma — 13 models, connection pooling (6 per process)
- Redis — quiz state cache, leaderboard cache, distributed locks, pub/sub
- BullMQ — phase advancement worker (1s tick), leaderboard aggregation, email delivery
- PM2 — multi-process clustering across 3–6 × 8vCPU servers
- Prometheus + Grafana + Loki — real-time monitoring and alerting
- Cloudflare — DDoS protection, CDN, R2 file storage
Quiz flow
- TV broadcasts QR code → viewer scans → lands in Lobby
- Quiz starts → Question phase (15 sec timer, time-based scoring 50–100 pts)
- Optional Sponsor Exposition (full-screen video ad)
- Waiting phase (answers locked, suspense builds)
- Standings revealed (live leaderboard, 10 sec)
- Repeat 16× → Completed → final rankings
Aggregate leaderboard: "Best 10 of 16" scoring — must complete 10+ quizzes to appear in final standings.
Screenshots
| Question Screen | Standings Screen |
|---|---|
![]() |
![]() |
Prizes
- Grand prize: Trip for 2 to LA 2028 Summer Olympics (flights, hotel, tickets)
- Runner-up: Peak brand Milano-Cortina 2026 Olympic winter jackets
- Daily draws: VIP Planica ski jumping tickets, Ford Explorer test drives, Peak hoodies
Sponsor integration
- Mid-quiz video ads: Full-screen sponsor exposition between question rounds
- Result page overlays: Video ads with frequency capping (sessionStorage-based cooldown)
- Random fallback: If no quiz-specific sponsor, random sponsor video plays
- Sponsors reach thousands of engaged, non-skippable viewers at peak attention moments
Infrastructure
| Component | Spec |
|---|---|
| App servers | 3–6 × 8vCPU (Digital Ocean) |
| Database | PostgreSQL Premium 4vCPU / 16GB |
| Cache | Redis Premium 4GB |
| Load balancer | DO LB + Cloudflare Pro |
| Monthly cost | €713–€1,351 (~€0.06/user) |
| Deployment | GitLab CI/CD, zero-downtime PM2 reload |
Performance
Load tested with k6 on local MacBook Pro M1 (4,000 VUs):
- 1,066,605 total requests — zero failures
- 2,959 req/sec sustained throughput
- 108 ms median response time
- 98.4% of requests under 1 second
- 18% latency improvement between test iterations (Jan 12 → Jan 16)
Related projects
JT Vault
Financial management and business operations platform for digital agencies
Tela
AI design system that turns natural language into production-ready HTML
JT i18n
Self-hosted translation management system for multi-format, multi-locale apps
Want something like this?
Tell us what you need and we build a working prototype first, so you see it before you pay for it.

