Skip to content
Games by Harring

Making of

HeadToHead: shipping a real iOS game with React, Capacitor and Firestore

The making of HeadToHead, my free football prediction game — one shared TypeScript engine for app and server, Firestore for live leagues, and a 30-second core loop.

Halfdan Harring

Halfdan Harring

Indie game developer · Aarhus, Denmark

HeadToHead was the game that proved my whole approach: that one developer with web tools can put a real, polished, social game on the App Store — and keep it running through full football seasons.

The idea: betting minus the money

Football prediction games existed, but they all pulled toward gambling. I wanted the opposite energy: a game you play with your friend group because nothing is at stake except being right. You pick home win, draw or away win for every match in the round, add a goalscorer guess if you’re brave, and the mini-league table remembers everything.

The design constraint that shaped every screen: a full round of picks must take about 30 seconds. The game’s depth lives in the season, not the session. No feeds, no tactics theatre.

The architecture: one engine, everywhere

HeadToHead is a monorepo with a shared TypeScript package at the center — the engine. Scoring rules, pick validation, booster math, league standings: all of it lives in one place and runs in two:

  • In the app, so the UI can show you exactly what a pick is worth, instantly.
  • In Cloud Functions, where rounds are settled when matches finish.

The points you see before kickoff and the points you get after are computed by the same lines of code. No drift, no “app says 12, server says 9” bugs — a whole category of multiplayer pain deleted by a folder structure.

Firestore as the multiplayer backend

I didn’t write a realtime server. Firestore is the multiplayer layer: picks, leagues and results are documents; the leaderboard updates because clients subscribe to them. When a round settles, a Cloud Function recomputes standings in one pass, and every phone in the league sees the table move.

Fixtures and results flow in from a football data feed on a schedule. Four leagues run simultaneously — the Danish Superliga, the Premier League, La Liga and Serie A — and season rollover is data work, not code work.

Capacitor: the web app that’s a real app

The app is React + Vite + Tailwind, wrapped with Capacitor into a genuine native iOS shell. App Store review, push-capable, offline-tolerant. Players cannot tell, and that’s the point. The wrapper approach gets criticized by people who haven’t shipped with it; what I know is that HeadToHead went from repo to App Store as one codebase, and that every fix I write lands in both worlds.

What the season taught me

Live-ops for a sports game is a rhythm: rounds open, rounds lock, rounds settle, repeat — every week, whether you feel like it or not. Automating that rhythm end-to-end (feed → fixtures → lock times → settlement → standings) was the difference between a game and a part-time job. The lesson I carry into every game since: the launch is an event, but the operation is the product.

HeadToHead is free on the App Store, with the companion site at headtohead.dk. Bring your loudest friend.