DEV Community

kareemblessed
kareemblessed

Posted on

Why Not Build a World Cup Wrapped? Nothing Was Stopping Me, So I Did

This is a submission for Weekend Challenge: Passion Edition

What I Built

As I sat there watching the World Cup for the tenth hour in a row, snacks long gone, dignity mostly intact, I thought, why not build a World Cup Wrapped? What is stopping me?

LandingPage

Turns out, nothing, except sleep and a healthy fear of JSON parsing errors.

FanSaga is Spotify Wrapped for football fans.

You pick your team's flag out of a grid of every qualified nation, answer two quick questions about how you experienced the tournament, and the app turns your team's actual real match history into a five slide story where you are the main character.

Somehow a scoreless group stage draw against Curaรงao becomes a defining moment of personal growth. I did not expect to feel something about a spreadsheet of goals and dates, and yet here we are.

At the end you get an animated slide player with music, a shareable recap card, and a whole highlight reel you can export and send to whichever group chat still argues about football at two in the morning.

Demo

Live app

Go pick a flag, answer the two questions, and let Gemini write your saga.
No sign up, no fee, no fine print, just football feelings.

Code

Github Link
Three parts of this build actually mattered. Here is how I did each one.

Turning real stats into a story, not a stat sheet

I did not want Gemini improvising scores, so the whole real match history for the fan's team gets dropped straight into the prompt as JSON, and Gemini is only allowed to write around it.

The last slide is the one doing the heavy lifting, it has to invent a Wrapped style persona from the team's actual run:

Making sure Gemini's JSON never breaks the app

Even with responseMimeType: application/json, Gemini sometimes stops one bracket short of a valid response. Instead of crashing the whole flow over a missing ], I walk the string, track every open bracket in a stack, and quietly close whatever got left open:

for (const ch of text) {
  if (ch === '"') inString = !inString
  else if (!inString && (ch === '{' || ch === '[')) stack.push(ch)
  else if (!inString && ch === '}' && stack.at(-1) === '{') stack.pop()
  else if (!inString && ch === ']' && stack.at(-1) === '[') stack.pop()
}
return text + stack.reverse().map((c) => (c === '{' ? '}' : ']')).join('')
Enter fullscreen mode Exit fullscreen mode

The fix was to stop racing React, mount every frame off screen at once, let it all settle, then capture in a separate second pass:

const mounted = nodes.map((node) => mountNode(node))
await settle(mounted)

const gif = GIFEncoder()
for (const { container } of mounted) {
  const canvas = await html2canvas(container, { backgroundColor: '#05050a', scale: SCALE })
  addFrame(gif, canvas)
}
Enter fullscreen mode Exit fullscreen mode

How I Built It

The app is React and Vite on the front end, styled with Tailwind and animated with Framer Motion so screens actually feel alive instead of just appearing.

Google's Gemini API is the brain of the whole thing.

It reads the real match data for your team and your two short answers, then writes the five slides of your saga, including a Wrapped style fan persona at the end.

That is the Google AI part of this build, and it is doing the most important job in the app.

I originally added ElevenLabs to narrate each saga out loud, and it sounded great, until the free quota ran out mid demo like a narrator fainting on stage.

Instead of fighting a quota bar, I dropped narration and leaned on twelve licensed music tracks across six moods instead. Turns out a good beat covers for a missing voice better than expected.

What You Actually Get

Pick a mood first, stadium, hype, dramatic, fun, chill, or groovy, and a matching background track starts playing behind your saga right away, no narrator needed.

Then your saga plays out as five cards, one at a time, walking through your team's real journey and the moment that hit you hardest, basically a movie trailer for your own fandom.

5 Cards

It ends on one final card with your fan persona and a "Top Percent of Fans" badge, a small bragging right generated just for you, ready to screenshot and send.

Final Card

For sharing, I kept it simple on purpose.You get a clean image card and an animated GIF highlight reel, both downloadable and both shareable straight from your phone.

Share

No accounts, no video processing on a server, just Gemini writing the story and the browser doing the rest.

Prize Categories

Best Use of Google AI, since Gemini does all the heavy lifting here and I mostly just added emoji and vibes.

Your Saga Awaits

Your team already lived through the whole tournament.
Go find out what kind of fan it made you. ๐Ÿ† LINK

Top comments (0)