Building watchable digital twins of 64 World Cup games
Try it out here
World Cup 3D
A lot of us saw the cool World Cup data visualizations going around social media, like this one from Alexander Bogachev:
Source: Bogachev's tweet here
I wanted to make my own but go a lot deeper, so I went looking to see where the data comes from.<br>It turns out a bunch of companies sell data to sports teams and gamblers that they build using computer vision on live broadcasts . It works something like this:<br>Source: SoccerNetObject detection and OCR for players / ball tracking<br>Field geometry identification for coordinate transform (using the penalty box corners, center circle, goal lines)<br>Mix of inference and human tagging to log events (like passes, interceptions, shots, penalties)<br>There's a lot more complexity around event tagging and player tracking, which requires visual embeddings for the players to maintain a best guess of their position when their jersey number isn't visible, but this is the high level.<br>The completeness of this tracking data varies by provider. It generally falls into the following categories:
Each row<br>May include
Matches<br>one match<br>competition, date, home & away teams, stadium, score / outcome, # periods
Roster<br>one player<br>player name, team, position, jersey number, started?
Events<br>one event<br>event type, timestamp, duration, actor, detail (varies by type)
Tracking<br>one frame<br>each player's XY position, ball XYZ position
I found 5 providers that offer free data samples for developers. Here's a quick comparison of their level of completeness:
Source<br># Games<br>What<br>Frames/game<br>Link
PFF<br>64<br>Videos + events + tracking<br>~160k<br>Data
StatsBomb<br>426<br>Events + tracking<br>~3-4k<br>Data
Metrica<br>Events + tracking<br>~160k<br>Data
SoccerNet<br>550<br>Videos + raw CV data<br>~160k<br>Data
WhoScored<br>Many<br>Avg team X position (what Bogachev used?)<br>~100<br>Example game
Since I'm building high-resolution digital twins of the games, I need a minimum of ~2 data frames per second (~11k frames total). PFF data is the most complete and polished with XY coordinates for all players at 30Hz . This resolution allows us to do any kind of game analysis, or visualization, we want. The data also happens to be from the 2022 World Cup! Other sources had regional league games.<br>💡<br>Side note: SoccerNet seems to have the best scientific community around game data extraction with multiple publications and helpful public repos
Now let's dig into PFF's data. Here's what it looks like, per game:<br>Match: 1KB<br>Roster: 8KB<br>Events: ~16-20MB<br>Tracking: ~0.8-1.2GB<br>The event data is highly detailed. Below is an example event object from a Messi header initialBodyType:HE at 13:36 in the Argentina v France final:<br>"frameNum": 29098,<br>"period": 1,<br>"periodGameClockTime": 816.417238,<br>"game_id": 10517,<br>"game_event_id": 6739132,<br>"game_event": {<br>"game_event_type": "OTB",<br>"formatted_game_clock": "13:36",<br>"player_id": "1531",<br>"player_name": "Lionel Messi",<br>"shirt_number": "10",<br>"position_group_type": "RW",<br>"team_id": "364",<br>"team_name": "Argentina",<br>"start_time": 970.904,<br>"end_time": 970.904,<br>"duration": 0,<br>"home_team": 1,<br>"sequence": 48,<br>"home_ball": true<br>},<br>"possession_event": {<br>"possession_event_type": "PA"<br>},<br>"event_data": {<br>"sequence": 48,<br>"initialTouch": {<br>"initialBodyType": "HE",<br>"initialHeightType": "A",<br>"facingType": "G",<br>"initialTouchType": "S",<br>"initialPressureType": "N"<br>},<br>"possessionEvents": {<br>"possessionEventType": "PA",<br>"nonEvent": false,<br>"ballHeightType": "A",<br>"highPointType": "A",<br>"passType": "S",<br>"passOutcomeType": "C",<br>"targetPlayerId": 10715,<br>"targetPlayerName": "Julian Alvarez",<br>"targetFacingType": "B",<br>"receiverPlayerId": 10715,<br>"receiverPlayerName": "Julian Alvarez",<br>"receiverFacingType": "B",<br>"accuracyType": "S",<br>"pressureType": "N",<br>"createsSpace": false<br>So here's what we want to build: a "video player" for World Cup games , rendering, as faithfully as possible, a 3D stadium, field, fans, and players.<br>The architecture will be pretty simple:<br>API server providing a list of available games, player info, and video keyframes<br>Streaming endpoint to deliver XY coordinates for players as the user watches the game<br>Web viewer with a game selector and video player<br>We'll start with a 2D visualization. Claude did a great job parsing the PFF data spec and building a compressed / streamable data format with a simple player to watch the data in 2D. Each game came out to ~100MB split into ~600 10-second files that are requested by the client in sequence as you watch the game. I'm sure this could be optimized further - JSON is not an efficient format.
0:00
/0:12
1×
Very cool! Now let's make it 3D, which of course will require no additional data. We could just build a 3D stadium and 3D players and have them run around at the proper XY coordinates, but this breaks down in cases where the players aren't running, e.g. corner kicks, headers, and injuries.<br>Also, our player position data is scraped from live broadcasts. On average, for 27% of broadcast airtime the ball is not in...