Inspiration
I've spent over five years working at pools as a lifeguard, and over that time I built a close connection with many of my coworkers — including the swim team coaches and meet directors. Twice a week, without fail, I'd watch them hunched over a laptop for hours, manually sorting through athlete times and hand-crafting meet lineups. It was tedious, error-prone work, and it always struck me that it was exactly the kind of problem a computer should be solving. Swimming Stacks is my answer to that.
What it does
Swimming Stacks is a browser-based meet lineup builder for competitive swim teams. Coaches upload one or more CSV files of athlete times, enter their team acronym, and the app automatically generates an optimized event lineup. Key features include:
- Smart athlete assignment across individual events using a multi-pass points-maximizing optimizer
- Head-to-head scoring projections when an opponent team is entered, showing predicted score splits per event and an overall win/loss projection
- Two relay teams per relay event, with Team A and Team B built from the fastest available swimmers and scored independently
- Cross-stroke fill-in for events with no direct entries, using related stroke times as a proxy
- Tie-splitting using the standard dual-meet point structure with tied places averaged across the tied positions
- A persistent local database so athlete times survive page refreshes without any backend
How we built it
The entire app is a single self-contained HTML file — no frameworks, no build tools, no backend. Just vanilla HTML, CSS, and JavaScript, with Google Fonts pulled in via CDN. Athlete data lives in localStorage.
The lineup optimizer runs in four passes:
- Pass 1 — Greedy point optimization. For each candidate swimmer in each event, compute their projected place given the current field. The optimizer iterates until the assignment is stable (capped at 30 iterations to prevent cycling).
- Pass 2 — Cross-stroke fill-in. Any event still empty after Pass 1 gets filled using swimmers with times in related strokes, marked with a
★ auto-filledflag. - Pass 3 — Minimum swim guarantee. Every athlete is ensured at least 2 events.
- Pass 4 — Relay construction. Up to two relay teams are built per event from remaining eligible swimmers, with Team A taking the fastest available legs.
Points are awarded as:
- 1st place: 5 pts
- 2nd place: 3 pts
- 3rd place: 1 pt
- 4th+: 0 pts
When swimmers tie, the points for the tied places are split evenly:
$$P_{\text{tied}} = \frac{1}{j - i}\sum_{k=i}^{j-1} P_k$$
where i is the first tied rank and j - 1 is the last.
Challenges we ran into
The hardest problem was event name normalization. CSV exports from different meet management systems use wildly inconsistent naming conventions — for example:
"Girls 15 & Over 50 Freestyle"vs"Girls 15&O 50 Free"vs"Women 15+ 50 FR"
All three refer to the same event. The app resolves this with a fuzzy token-matching system that scores candidates across four dimensions — gender, age group, distance, and stroke — and falls back gracefully when no confident match is found.
Getting the optimizer to converge without over-assigning swimmers across events also required care. The greedy loop reruns until the assignment is fully stable, with an iteration cap to avoid infinite cycling in edge cases.
Accomplishments that we're proud of
Shipping a genuinely useful, real-world tool as a zero-dependency single HTML file feels like an accomplishment worth noting. No login, no install, no cloud open it and it works. The dual-meet scoring projection, with proper tie-splitting math, turned out more accurate than expected for realistic meet scenarios. Watching it correctly handle a full event card, relays included, on the first build was satisfying.
What we learned
Even "simple" frontend work gets interesting fast once real-world messy data enters the picture. Fuzzy string matching, optimizer convergence, and age-group eligibility edge cases each took more thought than anticipated. It was also a good reminder that clean UI layout isn't just cosmetic it directly affects whether a coach can trust and act on the output at a glance.
What's next for Swimming Stacks
- Manual swimmer swap UI — let coaches override the optimizer and pin athletes to specific events
- Export to printable heat sheet — a clean flat table formatted for poolside use (event → heat → lane → name → time)
- Multi-meet time aggregation — pull in times from an entire season rather than a single CSV snapshot
- Age-up handling — automatically promote athletes who age up mid-season into the correct bracket
Log in or sign up for Devpost to join the conversation.