"Every game leaves a trace. This is how you read it."
Fig 01 — Select a game above to animate Wembanyama's shot attempts on a real NBA half-court. Data via NBA Stats API. Orange filled = made · Ring = missed · Hover any dot for shot details.
The shot chart doesn't just show makes and misses. It shows the geometry of a player's game — the zones they dominate, the spots they avoid, the corners where they're quietly more efficient than anyone mentions. With Wembanyama, the shape is unlike any player the league has seen: rim finishes, corner threes, and pull-up threes from step-back range coexist in a single season profile. Every dot is a decision. The pattern is the player.
Each dot is a player positioned by their career score — a composite metric from my ML model. Hover to explore. Click any dot to see who they're statistically closest to.
Fig 02 — Career Score Comparison. Scores computed by ML model trained on per-game stats, games played, and award history. Click any dot to animate nearest neighbors.
The model produces a single number that collapses a career into something comparable. It's not perfect — no number is — but when you click a player and see who slides toward them, you're seeing the model's opinion about statistical kinship. Sometimes it confirms what everyone knows. Sometimes it surprises you.
"Can a stat line be predicted before it happens?"
Can a player's counting stats — points, rebounds, assists — be predicted from recent form and matchup context alone? This model treats each player-game as a forecasting problem: given how someone has played over their last 5 and 10 games, and who they're about to face, what should we expect next?
Fig 01 — Feature pipeline. Each stage feeds the next, and nothing downstream ever sees a game the model has not already passed.
Rolling averages and volatility (5-game and 10-game windows) are computed for every target stat, alongside minutes, shot attempts, and free throw rate. A simplified usage metric approximates each player's offensive involvement.
Opponent context — what a given team typically allows at each stat — is layered in to produce matchup-adjusted features: not just "how has this player performed," but "how has this player performed relative to what this opponent gives up."
A scikit-learn pipeline handles preprocessing — standardization for numeric features, one-hot encoding for categorical ones — feeding into a Random Forest Regressor, tuned conservatively (max depth of 4) to avoid overfitting on noisy per-game data.
Training uses a strict time-series split: the model only ever trains on past games and validates on future ones, avoiding the lookahead bias that sinks a lot of naive sports models.
Every prediction below was made before the game was played and graded against what actually happened, stat by stat, based on how close the forecast landed to the real result.
Fig 02 — Average prediction accuracy by stat, based on how close each forecast landed to the actual result.
Each stat is plotted on its own scale — pooling points with three-pointers would manufacture a correlation that isn't there. The diagonal is a perfect prediction; the tighter the cloud hugs it, the better the model read that stat.
Fig 03 — Predicted vs. actual, by stat. r is the Pearson correlation within that stat. Points is the smallest sample and should be read with that in mind.
R² expresses how much of the variance in an outcome a model accounts for — an R² of 0.7 means roughly 70% of the game-to-game variation is explained. In live sports that is a brutally high bar. A single player's output swings on shot luck, foul trouble, blowout minutes, rotation changes and in-game matchup switches, and most of that is invisible to any feature set built from prior box scores.
Measured against one player's own variance over a handful of validation games, R² can read negative even when the predictions land close in absolute terms — the denominator is simply too small for the metric to behave. That is why this model is judged on the thing that survives that problem: how tightly its predictions track what actually happened, stat by stat.
The full graded record — strong runs and weak ones alike. Select a player to see every prediction the model made on them.
Fig 04 — Complete backtest log. Players ordered by sample size.
The spread between players is the honest part. Some profiles the model reads well — stable minutes, consistent role, predictable volume. Others it reads badly, and those tend to be the players whose usage swings with matchup or whose minutes move with foul trouble. Knowing which is which is the useful output, more than any single accuracy number.
"The stopwatch captures everything. These tools are how we listen to it."
Fig 01 — Miami GP · Track Animation. Racing line and sector progression through the Miami International Autodrome, built from FastF1 telemetry data.
Fig 02 — Canada / Jeddah / Miami 2025 · Race fastest laps for Leclerc, Stroll, and Piastri. Speed, throttle, and brake channels extracted via FastF1. Scrub the timeline or hover to compare drivers at any point on the lap.
A two-second gap at the flag looks like one thing in the standings and a completely different thing in the data. The Lap Time Analyzer pulls real race telemetry via FastF1 and breaks every lap into sectors — showing not just who was faster, but at which corner, on which lap, under which tire compound the delta grew.
Fig 02 — Teammate fastest lap comparison · Miami 2026 · Speed channel. Green fill = ANT carrying more speed · Red fill = RUS carrying more speed. Hover or scrub to explore every moment of the lap.
The teammate comparison is the cleanest experiment in motorsport — same machinery, same strategy, different driver. The Driver vs. Teammate Tool neutralizes the car advantage and isolates individual pace across qualifying and race stints.
When one driver is consistently 0.3s faster through the high-speed complex and 0.1s slower under braking, that's not noise. That's a skill profile.
"Seeing is easy. Understanding is built."
Fig 01 — Wembanyama Step-Back · Model Output. Trajectory overlay computed frame by frame: ball detection, arc path, release angle, and peak annotated in real time. OpenCV + custom tracking pipeline · 59 fps.
This pipeline applies computer vision to one of the most unguardable shots in the NBA — Wembanyama's step-back jumper. Frame by frame, it extracts ball position, calculates trajectory, and annotates the flight path with release angle and arc data. The output makes visible what broadcast angles obscure: the precise geometry that makes this shot so hard to contest.
Applied YOLO detection and SORT tracking to Tyrese Maxey footage, locking a persistent bounding box onto him through contact, screens, and full-speed cuts. At Maxey's pace, maintaining ID integrity across frames is a genuine technical challenge — one that pure detection models fail under pressure.
The annotated output logs position and proximity to other tracked players on every frame — the raw material for automated spacing and off-ball movement analysis that no box score captures.
CS student, data scientist, live sports broadcaster — in that order, depending on the day.
Driven by a fierce passion for competition, my goal is to join a forward-thinking sports organization where relentless preparation and data drive competitive edge. I focus on turning raw datasets into applicable tactical advantage. Building computer vision tracking tools, predictive models, and custom data workflows that directly inform decision-making. Through hands-on sports analytics projects and coursework in algorithms, computer science, and statistics, I combine deep technical precision with a high-stakes competitive mindset to help teams win.
MATH & STATISTICS
MACHINE LEARNING & ALGORITHMS
PROGRAMMING & SYSTEMS
Live Event Operator — EDGIO
September 2020 – June 2025 · Remote
Collaborates with major national sports broadcasters on live programming delivery. Manages software tools for live transitions, ensuring minimal downtime under real broadcast pressure. Front-row seat to how sports media actually operates.
Research Intern — Foul Territory
September 2025 – Present · Remote
Built the MLB analytics pipeline behind Foul Territory's broadcast rundowns, including a recurring Gameday Research deliverable — storylines, clinch scenarios, and pitching matchups that hosts reference live on air.
University of the District of Columbia
B.S. Computer Science · Expected 2027 · GPA 3.5
Open to work, collaborations, and conversations about sports data.
This scouting lab breaks down hitter performance using Statcast data. Use the zone grid to see where a player makes contact, the EV histogram to assess bat speed, and the stats table to compare month-to-month trends. Higher barrel% and hard-hit% indicate quality contact.
Fig 01 — Statcast batted ball data · 2D: pitch speed vs exit velocity (red shading = 95+ mph hard contact) and launch-angle profile · 3D: every batted ball plotted as its own physics parabola from contact through flight, coloured by contact type · Click a point to view velocities/angle · Filter by pitch type or contact result above.
Hitting is only half the matchup. A pitcher's identity lives in where the ball leaves his hand and how it moves on the way to the plate. Release point reveals arm slot and repeatability — a tight cluster means a delivery a hitter can't read pitch-to-pitch. Movement separates the arsenal: which offerings ride, which sweep, which drop off the table. Together they explain why the same velocity plays completely differently from two different arms.
Fig 02 — Statcast pitch data · 2D: release point (catcher POV) and horizontal vs vertical break (in) · 3D: every pitch drawn as a curved trajectory from release to plate, with break driving the curvature · Click a pitch to inspect velo, spin and movement · Filter by pitch type or result above.