A tennis player hits serve after serve in practice. The motion is drilled, filmed, corrected, repeated, and repeated again. Then the match gets tight and the serve disappears.
The usual explanation is mechanical: the serve broke down. The better detective story starts one step earlier. A serve is not one identical event repeated forever. The toss changes. The legs tire. The target changes. The score changes the body.
The same serve motion never exists twice; the causal question is which source of variation pushed the error over the line.
Share: A tennis serve is repetitive practice under changing toss, timing, fatigue, pressure, and target conditions. #Causality #Tennis #SportsAnalytics
This article uses a causal detective story to explain why high-volume practice does not eliminate serve errors.
Start with the apparent cause
Professional tennis moves into Rome in early May; the Association of Tennis Professionals (ATP) lists the Internazionali BNL d’Italia in Rome for May 6-17, 2026 [1]. Clay-court conditions, long rallies, and pressure games make the serve feel less automatic than it looks in practice.
The apparent cause is simple:
- the player missed because the serve mechanics failed
That is not wrong. It is just not specific enough to fix.
Biomechanics research treats the tennis serve as a variable movement, not a perfectly duplicated machine motion. Studies have examined motor variability, toss variability, serve accuracy, and pressure effects [2, 3, 4].
Put the suspects on the board
The directed acyclic graph (DAG), a graph whose arrows do not loop back on themselves, uses TossVariability, Fatigue, and RiskChoice as competing treatment suspects, with ServeError as the outcome:
PracticeVolume: repetitions and drillingFatigue: tired legs, shoulder load, and loss of precisionMatchPressure: score and consequenceTossVariability: how far the toss moves from the intended windowRiskChoice: how aggressive the target isContactPointError: mismatch between body and ballServeError: fault or weak serve outcome

Here is the graph as a py-scm setup. This uses the py-scm reference implementation for a continuous Gaussian structural causal model (SCM). An SCM defines each variable through direct causes. The coefficients are illustrative.
import numpy as np
from pyscm.reasoning import create_reasoning_model
nodes = [
"PracticeVolume",
"Fatigue",
"MatchPressure",
"TossVariability",
"RiskChoice",
"ContactPointError",
"ServeError",
]
weighted_edges = [
("PracticeVolume", "TossVariability", -0.55),
("PracticeVolume", "RiskChoice", 0.20),
("PracticeVolume", "ServeError", -0.15),
("Fatigue", "TossVariability", 0.45),
("Fatigue", "ContactPointError", 0.35),
("Fatigue", "ServeError", 0.20),
("MatchPressure", "TossVariability", 0.25),
("MatchPressure", "RiskChoice", 0.25),
("MatchPressure", "ServeError", 0.25),
("TossVariability", "ContactPointError", 0.90),
("RiskChoice", "ServeError", 0.50),
("ContactPointError", "ServeError", 0.80),
]
idx = {node: i for i, node in enumerate(nodes)}
B = np.zeros((len(nodes), len(nodes)))
for parent, child, weight in weighted_edges:
B[idx[child], idx[parent]] = weight
A = np.eye(len(nodes)) - B
cov = np.linalg.inv(A) @ np.eye(len(nodes)) @ np.linalg.inv(A).T
model = create_reasoning_model(
{"nodes": nodes, "edges": [(p, c) for p, c, _ in weighted_edges]},
{"v": nodes, "m": [0.0] * len(nodes), "S": cov.tolist()},
)
The graph says practice helps by reducing toss variability and direct error. It does not make fatigue or pressure disappear.
Test the suspects
The inference code compares three intervention contrasts:
toss_effect = model.equery(
"ServeError",
{"TossVariability": 1.0},
{"TossVariability": 0.0},
)
fatigue_effect = model.equery(
"ServeError",
{"Fatigue": 1.0},
{"Fatigue": 0.0},
)
risk_effect = model.equery(
"ServeError",
{"RiskChoice": 1.0},
{"RiskChoice": 0.0},
)
The first query changes toss variability. The second changes fatigue. The third changes target risk.
Observed toss-variability gap: +0.94
Intervention toss effect: +0.72
Observed fatigue gap: +0.80
Intervention fatigue effect: +0.80
Observed target-risk gap: +0.50
Intervention target effect: +0.50
In this toy model, toss variability has a large causal effect through contact-point error. Fatigue has a large effect because it changes both toss and contact. Target risk matters, but it is a different fix.

The landing chart shows the intuition. Practice landings are tighter. Pressure landings widen and drift. The target window did not move; the distribution did.

How players overcome the error
The model points to different interventions:
- reduce toss variability with a routine that makes the toss cancellable when it leaves the window
- reduce fatigue effects by practicing serves after physical load, not only at the start of a session
- reduce target risk by choosing bigger margins on second serves and pressure points
- practice pressure explicitly so
MatchPressureis not a new parent in the graph on match day
The fix is not “serve more” by itself. The fix is to practice the sources of variation that appear when the serve actually matters.
Sources
- Internazionali BNL d’Italia, ATP Tour, accessed April 28, 2026.
- Relationship between motor variability, accuracy, and ball speed in the tennis serve, PubMed, accessed April 28, 2026.
- Biomechanical case study on toss up motion in tennis serve, International Society of Biomechanics in Sports, accessed April 28, 2026.
- Preventing a loss of accuracy of the tennis serve under pressure, PLOS One, accessed April 28, 2026.
Download the runnable standalone Python example: Python example ZIP.

Leave a Reply