MEME ARENA

Two memes enter. Real tweets decide who survives.

A fighting-game character-select screen for internet culture, where every battle is decided by fresh mention data from tens of millions of real tweets on X.

Inspiration

We built this for the memetics track, and because of our families. Every one of us has had a parent overhear "skibidi," "6-7," or "tung tung tung sahur" and ask what language we're speaking. The problem isn't that memes are meaningless. It's that their popularity is invisible. You can't show your mom that "labubu" is having a bigger week than "sigma."

What it does

Two people each pick from 51 memes and hit Start Battle. They fight in an arcade-style arena with health bars, a live fight feed, background music, and an ElevenLabs announcer calling the match (“SKIBIDI TOILET versus LABUBU: FIGHT!”).

The winner comes from real data. We count recent mentions of each meme in the X firehose, with extra weight on newer posts, then play those totals out over about 19 seconds. The punches, combos, and health bar follow that stream, so it looks like a live fight as the database is scanned.

Every fight is saved, so memes pick up a W/L record, a leaderboard, and a champion belt. You can also download a share card of the final score.

How the score works

Raw mention counts are a bad trending signal, because an old viral meme would win on volume alone. So each meme's score is a recency-weighted sum over every matching tweet, computed in SQL:

$$S_m = \sum_{i \in \text{mentions}(m)} e^{-\lambda (T - t_i)}, \quad \lambda = 2\times10^{-5}\,\text{s}^{-1}$$

Here \(T\) is the newest tweet in the dataset and \(t_i\) is when mention \(i\) was posted. That gives a half-life of about 9.6 hours: a mention from this morning is worth twice one from last night, and last week's spam is worth almost nothing. Health bars show each meme's share of the total:

$$\text{HP}_L = \frac{S_L}{S_L + S_R} \times 100$$

How we built it

  • Data: The HopHacks firehose (396 Parquet files, 395M tweets, 21GB+). We filtered it with DuckDB and bulk-loaded 110M rows into TigerData (TimescaleDB), then kept it current with a real-time pipeline that streams fresh tweets from X into the same database, so time-decayed battle scores reflect what's trending right now.
  • Backend: FastAPI + psycopg2. The battle endpoint runs the decay-scored query per matchup, a battle_records table stores history, and ElevenLabs generates announcer lines on the fly. Gemini API generates a historical summary of each meme passed in.
  • Frontend: React + Vite. A custom battle engine handles projectiles, combo streaks, procedural commentary, and easter eggs (try landing a health bar on 67, 69, or 21). Web Audio provides the chiptune SFX, and a canvas renders the share card.

Challenges we ran into

  1. Meme or just a word? Labeling tweets was messy because a lot of terms are ordinary language first. 67 is a number in most posts and a meme in some; same trap for words like “aura” or “sigma.” We had to iterate patterns so we counted meme uses, not every random mention.
  2. Loading the Data It was challenging loading all the data into TigerDB due to the size of the database. Each section we did took about an hour to finish.
  3. Live battles are finicky. Getting the totals was only half of it. During the fight, the mention counts, health bar, attacks, and commentary all have to stay together, and they kept drifting apart. The bar would freeze, drop to 0% on a meme that still had mentions, or fire an attack when nobody’s count had moved.

What we learned

  • Getting data from a database in real time. We learned how to structure queries and indexes so results come back fast enough to feel live, not just correct.
  • Fun frontend design. We learned how to take raw data and numbers and turn them into something visual and playful that people actually want to watch.
  • How to work on a team and communicate ideas with each other.

Limitations

Mentions are matched by keyword, so ambiguous terms can add noise. Data covers one platform and one month. The fight animation is a replay of real totals, not a live stream.

What's next

  • Full trend model. Treat "trending" as a control problem, combining level, momentum, and history with engagement- and source-normalized weights:

$$\text{Trend}(t) = K_p P(t) + K_i \int_0^t P(\tau)\,d\tau + K_d \frac{dP}{dt}$$

  • Live polling of fresh sources mid-battle
  • Decay rates fit per meme category with ML
  • Tournament mode: 51 memes, one bracket, one champion

Built With

Share this project:

Updates

Submission history