How I counted
- Volunteers chart pro matches point by point. The Match Charting Project has charted serves.
- Each serve becomes one row: side, direction, score and who won the point.
- Tiebreaks ( of serves), serves with no direction () and no-ad deciding points are left out. That leaves serves.
- Each player is compared with a typical pro who serves with the same hand, against the same mix of opponents.
- Every test is checked for lucky results across all players (false discovery rate ).
A player is listed with at least charted 1st serves on each side and at least charted matches. That gives players and of their serves. A typical pro means all players who serve with the same hand, the player included, weighted to the same mix of righty and lefty returners. Every share and win rate has a range that allows for serves in the same match being alike.
Across all players
On 1st-serve break points, a typical pro goes wide a little more often than on other points in the same match, about more serves out of for men and for women (formula 11).
Pros also switch direction more often than a random order would. Men's 1st serves repeat the last direction on fewer serves out of than the same serves shuffled into random order, and women's on fewer (formula 12).
The formulas
These are the 14 formulas the code runs, each with a link to the code.
The idea being tested
w_{\text{wide}} = w_{\text{body}} = w_{T}If a server mixed perfectly (minimax play, Walker and Wooders 2001), every direction would win the same share of points, so a returner would have nothing to exploit. A 1st serve's "win" counts the whole point, including points where the 1st serve missed.
Estimates formulas 1 to 5
1 Player share and win rate
\begin{aligned}&\hat p=\frac{\sum_g y_g}{N},\quad N=\sum_g m_g \\ &\widehat{SE}=\max\Big(\sqrt{\tfrac{G}{G-1}\sum_g\big(\tfrac{y_g-\hat p\,m_g}{N}\big)^2},\ \sqrt{\hat p(1-\hat p)/N}\Big) \\ &\text{CI}=\big[\max(0,\hat p-t_{.975,G-1}\widehat{SE}),\ \min(1,\hat p+t_{.975,G-1}\widehat{SE})\big]\end{aligned}A player's matches are pooled, and the standard error treats each match as a cluster but is never smaller than the plain binomial one. For a share, y counts serves to direction d and m counts serves in the situation. For a win rate, y counts points won after serving d and m counts serves to d.
2 Favorite-target share and its percentile
f=\max_d \hat s_d,\qquad \text{pct}_i=\frac{\#\{j\neq i: f_j<f_i\}}{n_{\text{group}}-1}The favorite-target share f is the share of serves to the most-used direction on all points. The percentile is the share of the other listed players in the same tour, serve and side whose f is lower.
Code: summarise in src/app_data.py; percentile in load in src/export_site.py
3 Typical pro against the same opponents
\bar s_d=\sum_k\pi_k\,s^{L}_{k,d},\qquad \bar w_d=\sum_k\pi_k\,w^{L}_{k,d}The shares and win rates of all pros for each handedness matchup k are weighted by the player's own share of serves in that matchup. These typical-pro values pool break points and other points and include the player.
4 Break-point shift
\Delta_d=\frac{n^{BP}_d}{N^{BP}}-\frac{n^{\neg BP}_d}{N^{\neg BP}}The share of serves to direction d on break points minus the share on all other points. For example, Rafael Nadal's deuce-side 1st serves went wide on of other points and of break points.
5 Upper-bound gain
\text{gain}=100\,(\hat w_{d^*}-\hat w_{\text{all}}),\qquad d^*=\arg\max_{d:\,n_d\ge30}\hat w_dPoints per serves the server would gain by sending every serve to the direction that won most for them. It counts only when the win test has q < , and it is an upper bound: it ignores that returners adapt and it picks the best of several noisy rates.
Tests and corrections formulas 6 to 8
6 Mix compared with a typical pro
\begin{aligned}&u_g=\tfrac1N\begin{pmatrix}n_{g,\text{wide}}-m_g\hat s_{\text{wide}}\\ n_{g,T}-m_g\hat s_T\end{pmatrix},\quad \hat V=\tfrac{G}{G-1}\sum_g u_gu_g^\top,\quad \delta=\begin{pmatrix}\hat s_{\text{wide}}-\bar s_{\text{wide}}\\ \hat s_T-\bar s_T\end{pmatrix} \\ &W=\delta^\top\hat V^{-1}\delta,\qquad p=\Pr\big(F_{2,\,G-1}>W/2\big)\end{aligned}A Wald test of whether the wide and T shares (body is the remainder) differ from the typical-pro values, with a match-clustered covariance. The typical-pro values are treated as fixed and there is no binomial floor.
7 Generalized Cochran-Mantel-Haenszel test
\begin{aligned}&D_i=\sum_h\Big(y_{hi}-n_{hi}\tfrac{S_h}{T_h}\Big) \\ &V_{ij}=\sum_h\frac{S_h(T_h-S_h)}{T_h-1}\Big(\frac{n_{hi}}{T_h}\delta_{ij}-\frac{n_{hi}n_{hj}}{T_h^2}\Big),\ i,j\le k-1 \\ &Q=D^\top V^{-1}D\ \sim\ \chi^2_{k-1}\end{aligned}The strata h are matches with more than one serve, so differences between matches cancel. In the win test the groups are directions with at least serves and success is winning the point. In the break-point test the groups are break points and other points and success is serving to d. The p-value is the asymptotic chi-square one.
Code: cmh in src/app_data.py
8 Benjamini-Hochberg false discovery rate
q_{(i)}=\min_{j\ge i}\ \min\Big(1,\frac{m\,p_{(j)}}{j}\Big)Each family is pooled across tours, serves and sides: m = mix tests, win tests and break-point tests. A test counts as clear when q < . There are clear break-point tests on player, side and serve combinations.
Code: bh in src/app_data.py
Estimates across all pros formulas 9 to 12
9 Two-way clustered variance
M_c=\frac{G_c}{G_c-1}\sum_{g\in c}\Big(\sum_{i\in g}u_i\Big)\Big(\sum_{i\in g}u_i\Big)^{\top},\qquad M=M_{\text{srv}}+M_{\text{ret}}-M_{\text{srv}\times\text{ret}}Standard errors across all pros allow for correlation within the same server, the same returner and the same server-returner pair. If the combined matrix is not positive semidefinite, the one-way matrix with the larger trace is used. Degrees of freedom are the smaller of the server and returner cluster counts minus one.
10 Direction ranges across all pros
u_i=\frac{y_i-\hat p\,n_i}{N},\qquad \hat p\pm t_{.975,\min(G_s,G_r)-1}\sqrt{M_{11}}\ \text{ clipped to } [0,1]Typical-pro shares and win rates use the two-way clustered variance above, with no binomial floor.
11 Within-server comparisons
\begin{aligned}&\tilde y_i=y_i-\bar y_{f(i)},\quad \tilde x_i=x_i-\bar x_{f(i)},\quad \hat\beta=(\tilde X^\top\tilde X)^{-1}\tilde X^\top\tilde y \\ &u_i=\tilde x_i\,(\tilde y_i-\tilde x_i^\top\hat\beta),\qquad \hat V=\frac{N-1}{N-K}(\tilde X^\top\tilde X)^{-1}M(\tilde X^\top\tilde X)^{-1}\end{aligned}Each serve is compared only with serves in the same fixed-effect group: server by side by matchup by break point for points won and serve in, and match by server by side for the break-point shift. The regressors are body and T indicators with wide as the baseline, or break-point and game-point indicators with the outcome "served to d". K counts only these regressors.
12 Serial repeats
R_h=\#\{t:d_{h,t}=d_{h,t-1}\},\qquad E_h=\frac{\sum_k n_{hk}(n_{hk}-1)}{n_h},\qquad \text{diff}=\frac{\sum_h(R_h-E_h)}{\sum_h(n_h-1)}Within each match by server by side group (one tour and serve number, in point order, at least two serves), the number of serves that repeat the previous direction is compared with the number expected if the same serves came in random order. Consecutive 2nd serves are not consecutive points.
Replication formula 13
13 Split-half stability
f^{A}_i=\max_d\hat s^{A}_{i,d},\quad e^{A}_i=\hat w^{A}_{i,T}-\hat w^{A}_{i,\text{wide}},\qquad r=\operatorname{corr}_i(f^A_i,f^B_i)Each player's matches are shuffled once (seed 0) and the first half goes to half A. The favorite-target share f and the T-minus-wide win edge e are computed in each half from plain proportions on 1st serves, and correlated across players within tour and side. This is one split, with no confidence interval and no Spearman-Brown correction.
14 Serves needed to spot a lean
d=p_1-p_2,\qquad n=\frac{z^2\,(p_1+p_2-d^2)}{d^2},\quad z=1.96,\qquad m=n\cdot\frac{\text{matches}}{\text{serves}}p1 and p2 are the two largest lane shares on one side, on all points. The difference of two shares from the same serves has variance (p1 + p2 - d2) / n, so n is the number of serves at which the favorite's lead is as wide as its range (z from the normal curve). m turns that into matches at that side's own serves per match. It is a rule of thumb from the observed shares, not a power calculation.
Code: need in src/export_match.py and need in site/js/tool.js
How a call is scored
A call scores the share of that player's charted serves, from that side and in that situation, that went to the lane picked. Nothing is drawn at random, so the same call always scores the same. The typical pro's lane is the lane with the largest share for a typical pro facing the same returners; when its top two lanes are within three points of each other, both count and the bar averages the player's shares in them. The scouting edge is shown only when the side has a clear favorite, its mix of lanes differs from the typical pro's (the mix test, formula 6), and the favorite's range sits above the range of every other typical-pro lane. The explorer fills a dot on the same rule. The charts record where the serve went, never what the returner did.
Match model: how serves are charted from video
The match model in the match_model folder reads a broadcast video. The score bug gives the server, the side, the score (so break points can be flagged, though the demo clip has none) and the point winner; detected court lines put the ball bounce in meters; the bounce is then sorted into equal thirds of the service box width, measured from the center line, as out wide, at the body or down the middle. Without a bounce it falls back to the receiver's position, with lower confidence, and when neither settles it the lane is left blank. Volunteers who chart pro matches judge the lane by eye, and the two methods have not been compared on the same serves. The accuracy shown on the main page comes from the demo clip the rules were tuned on, so it is not a blind test. The model needs a Mac and a broadcast score bug, and it uses Ultralytics under the GNU AGPLv3.
What the charts can and cannot see
The charts record where each serve went (wide, body or T), whether it went in, who won the point and the score, so break points can be picked out. They do not record where the returner stood or leaned, what the returner expected, or the exact spot where the ball landed.
Favorite-target share is the share of serves to the server's most-used direction. A returner who always leaned that way would be right this often, and a server who split evenly across three directions would give . The mix test (formula 6) asks whether a server's mix of directions on a side differs from a typical pro against the same opponents, and counts it when q < .
Limits
- Volunteers choose which matches to chart. Most are big matches of well-known players, not a random sample.
- The data are observational. A server may pick a direction because of the returner's position or the score.
- Returners adapt, so any gain figure is an upper bound.
- A test that finds no difference does not show that there is none. A small edge may be too small to detect with this much data.
- Tiebreak points, serves with no recorded direction and no-ad deciding points are left out.
- Each rebuilt serve row was checked against the project's own serve-direction totals. The README reports the match rate.
Data and license
Data: Match Charting Project by Jeff Sackmann and contributors, licensed CC BY-NC-SA 4.0. The derived tables on this site (data/serveiq.json) carry the same license and are for non-commercial use only.
Code: MIT license. Source and analysis: github.com/ijunsu2082-hue/tennis-serve-iq. A backup version of this tool runs at serve-iq.streamlit.app.
Photo credits
Every player photo on this site comes from Wikimedia Commons under a free license. Each row links to its license and to the file page.
Show all photo credits
Photo credits load with the page.