You know what's fun? Watching people swim. You know what's not? Trying to keep track of swimming scores on paper while 40+ events are happening, the pool's echoing, and someone just dropped the heat sheet into the water.


That’s where Aquaclash ScoreApp dives in (yes, pun intended).


SLIIT’s annual inter-university swimming championship, Aquaclash, needed a serious tech boost. In 2024, Manula Cooray SLIIT’s swimming captain for 2025 pitched the idea
“Can we build a real-time score app for the event? So competitors, organizers, and spectators can all see the scores in one place?”
So, Challenge accepted.
Designed and deployed a fully responsive, real-time scoring web app tailored for live swimming tournaments. It replaced manual sheets with something fast, reliable, and actually usable from poolside.

Key Features

  • Real-time scoreboard for all users (no login required)
  • Admin panel secured via Google Auth
  • Updates on event results as they happen
  • Mobile-friendly & responsive UI
  • Public and live from the start
“This app was a lifesaver. We didn’t have to worry about writing and recalculating after every event. Everyone had access — swimmers, officials, even my uncle who watched it from home.” — Manula Cooray, SLIIT Swimming Captain (2025)
Screenshot

Tech Stack

  • Frontend: Next.js (App Router)
  • Design System: Shadcn + Tailwind
  • Backend & Auth: Supabase (Database + Google Auth)
  • Hosting: Vercel

Scoring Rules

interface EventResult {
  eventId: string;
  universityId: string;
  place: number; // 1 = Gold, 2 = Silver, 3 = Bronze
}

function getPoints(place: number): number {
  switch (place) {
    case 1:
      return 5;
    case 2:
      return 3;
    case 3:
      return 1;
    default:
      return 0;
  }
}

function calculateUniversityScore(
  results: EventResult[],
): Record<string, number> {
  const scores: Record<string, number> = {};
  for (const result of results) {
    scores[result.universityId] =
      (scores[result.universityId] || 0) + getPoints(result.place);
  }
  return scores;
}

Database Structure

 universities id, name, logo
 teams id, university_id (FK), event_id (FK)
 players id, name, university_id (FK), event_id (FK), team_id (nullable)
 events id, name, type (individual / relay), gender, order
 results id, event_id (FK), player_id or team_id (FK), place (1/2/3), timestamp
This setup supports both individual and relay events, assigning results either to a single player or a team.

System Architecture

[Client (Browser)] --> [Next.js Frontend] --> [Supabase (DB + Auth)]
  • Public Scoreboard: Static-rendered pages with client-side fetching
  • Admin Panel: Secured routes using Supabase’s Google Auth
  • Data Sync: Supabase’s real-time subscriptions used to reflect results instantly
  • No Offline Usage: The system is fully online-dependent, ensuring data consistency and real-time updates.

What I Learned

  • Real-time data sync is crucial for live sports — Supabase subscriptions made this seamless.
  • UI/UX matters: poolside users need big buttons, clear scores, and zero distractions.
  • Auth flows must be simple for admins but invisible for public viewers.
  • Testing with actual event data and feedback from swimmers/officials is invaluable.
  • Deploying fast, iterating with user feedback, and keeping things reliable is key for event tech.
Note: This app is currently for in-house use only. Soon, I'll make it public!