Case study N° 04 · Sports

SimWorld Sports platform

The league website, the coaches' live game-day portal and the media desk for a simulated basketball league network.

One site and one portal
2leagues
SWU plays halves and SW Prep plays quarters; schedules, box scores and brackets handle both.
Per base-strategy reset
27engine calls
Sent in seven batches, then logged to coaching_actions. A timeout also posts to the team’s Discord.
Region pairs priced for recruiting
15,799pairs
An offer’s cost is one lookup, from the player’s home region to the team’s.
SWU schedule page in the demo: a week picker on today, with final scores by half, records at tip-off, networks, venues and box score links
Fig. 1|The SWU schedule on invented data. Records are as of tip-off, and every number comes from the league’s own API routes and SQL.

Two leagues, one website

SimWorld Sports ran a simulated basketball world with a college league (SWU) and a prep league (SW Prep). The public site is a Next.js app on Postgres. It has a schedule for every day, conference standings with last-five records and streaks worked out in the API, team pages, box scores and tournament brackets.

  • Records at tip-off. Each game stores both teams’ records at the time it was played, so old box scores read the way they did that night.
  • Brackets from data. A bracket template is stored as JSON. Games carry a round and a slot, and the page fills the template from them.
  • Two scoring formats. SWU plays halves and SW Prep plays quarters, and the same box score component handles both.

A coach’s adjustment, end to end

Coaches signed in with Google or Discord. Only emails already in the league’s users table got in, and middleware checked each coach’s role and league. During a live game, Coaches Corner is the sideline. Every button runs the same four steps:

  1. Check in Postgres that the game is live and the team is in it.
  2. Send the settings to the game engine. Resetting to the coach’s base strategy is 27 requests in seven batches: points of emphasis, offensive sets, coach sliders, 17 defensive settings and the defensive game plan.
  3. Post a line to the team’s Discord locker room, as the coach.
  4. Log a row in coaching_actions with the request, the engine’s reply and the Discord message.

Before tip-off, coaches set presets for each player: starter, scoring option, ball handler, minutes and touches. They also set the team’s on-ball philosophy, pick-and-roll coverage and pressure.

Recruiting on a budget

Coaches recruit with a budget. An offer’s cost depends on how far the player is from the program, so it’s a single lookup in a 15,799-row table of home region to program region. The recruiting page lists the open offers, the players still available and what each one would cost.

The media desk

Before the web platform, I built the SimWorld Media Control Panel for the media team. It was a cross-platform Python desktop app (Kivy) for writing and publishing league content.

  • Recaps and graphics for all 32 teams, drafted with the OpenAI API from league data in Google Sheets.
  • Images hosted on Imgur and files kept in Google Drive.
  • A separate scheduler for recurring posts, plus installer scripts so non-technical staff could set it up themselves.

How these demos run

Both demos are the original apps, exported as static sites. The league data is invented: teams, arenas and events come from the repo’s own SQL, and every player, coach and score is generated.

  • The website. Its API routes and SQL ran unchanged at build time, against Postgres compiled to WebAssembly (PGlite). Their responses are served as JSON, with dates moved so that today always has games.
  • The coach portal. Its route handlers run in your browser against that same embedded Postgres. Requests to the game engine and to Discord are captured in a “Behind the scenes” panel instead of being sent.

Getting the old code running turned up bugs, now fixed:

  • Strategy page crash. It read the team from React state before that state was set, so it crashed in production builds.
  • Team schedule never loaded. Its data effect ran before the coach id was read from the URL.
  • Hook order. A hook came after an early return, which React rejects.
  • Missing columns. One stats route queried columns the players table doesn’t have.
  • Wrong links. Prep coaches’ dashboard links pointed to the college league’s pages.
  • Dates off by one. Game dates were shown a day early in US time zones.

Result

The site and the coach portal were built for the league’s fourth season. The player hub was never finished, so the demo shows its in-progress design on mock data.

Figures

Coaches Corner in the demo: next game and live status, team information and season averages, then pre-game presets for scheme, pick-and-roll coverage and each player’s role
Fig. 2|Coaches Corner during a live game: season numbers up top, then the pre-game presets a coach submits before tip-off.
Strategy controls with the demo’s Behind the scenes panel open, listing the game-engine requests that a Base Strategy press produced
Fig. 3|Pressing Base Strategy in the demo. The panel lists the 27 game-engine requests the portal’s own route code builds, which are captured here instead of sent.
Tournament page for the SWTV Classic Tipoff Invitational: an eight-team bracket with quarterfinal, semifinal and final results
Fig. 4|A tournament bracket, drawn from a stored bracket template and the games tagged with each round and slot.

Methods

Stack
Next.js 15, React 19, TypeScript, PostgreSQL (AWS RDS), NextAuth (Google, Discord), Tailwind, Discord webhooks, Python, Kivy, OpenAI API.
Data
The demo runs in your browser on sample data; nothing is sent anywhere.
Status
Shipped Oct 2025. SWU and SW Prep coaches, fans.