Case Study: Bill Walsh’s 49ers and the West Coast Offense

How a team that couldn’t yet run the ball made the short pass its running game, and how its coaches spread across the league.

Show the Python (shared helpers for this chapter’s diagrams)
import logging
logging.getLogger("matplotlib.font_manager").setLevel(logging.ERROR)
import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.patches import Circle, Ellipse, FancyArrowPatch, FancyBboxPatch, RegularPolygon
from gridiron import Play, Player, Field, offense, technique, style, routes as r

style.apply()
GREEN = "#0f7a55"          # darker step of the aqua highlight, for text on the light field
OL_KEYS = ("LT", "LG", "C", "RG", "RT")


def pdf_build():
    return os.environ.get("RTG_FORMAT", "").lower() in ("pdf", "typst", "print")


def O(key, pos, d, w, label):
    return Player(key, pos, "off", d, w, label=label)


def D(key, pos, d, w, label):
    return Player(key, pos, "def", d, w, label=label)


def line():
    """The five offensive linemen with one-letter labels, T G C G T."""
    short = {"LT": "T", "LG": "G", "C": "C", "RG": "G", "RT": "T"}
    return [p.moved(label=short[p.key]) for p in offense("singleback") if p.key in OL_KEYS]


def T(t, side=1):
    """Width of technique t on the offense's right (side=+1) or left (side=-1)."""
    return technique(t, side)


def split_backs(y_side=1, z_w=13.0, x_w=-13.0, h_side=-1):
    """Walsh's base look: split backs (the pro set), one back behind each tackle, a tight end, a split end (X)
    and a flanker (Z). The halfback (H) is on the offense's left by default; h_side=+1 puts him on the right."""
    return line() + [O("QB", "QB", -1.9, 0.0, "Q"), O("HB", "RB", -5.0, 3.2 * h_side, "H"),
                     O("FB", "FB", -5.0, -3.2 * h_side, "F"),
                     O("Y", "TE", -0.7, 4.8 * y_side, "Y"), O("X", "WR", -0.7, x_w, "X"),
                     O("Z", "WR", -1.5, z_w, "Z")]


def era_43(shell="one", ss_side=1):
    """A 1980s 4-3 Over against a pro set (tight end on the offense's right), corners 7 off."""
    dfn = [D("WE", "DE", 1.0, T("5", -1), "E"), D("WT", "DT", 1.0, T("1", -1), "T"),
           D("ST", "DT", 1.0, T("3"), "T"), D("SE", "DE", 1.0, T("5") + 0.2, "E"),
           D("SAM", "LB", 4.0, 6.4, "S"), D("MIKE", "LB", 4.5, 0.8, "M"), D("WILL", "LB", 4.5, -3.0, "W"),
           D("LCB", "CB", 7.0, -13.5, "CB"), D("RCB", "CB", 7.0, 13.5, "CB")]
    if shell == "two":
        dfn += [D("FS", "FS", 13.0, -8.0, "FS"), D("SS", "SS", 13.0, 8.0, "SS")]
    else:
        dfn += [D("FS", "FS", 13.0, 0.0, "FS"), D("SS", "SS", 8.0, 7.5 * ss_side, "SS")]
    return dfn


def go_to(p, key, end, kind="block", delay=0.0, speed=None, via=()):
    """Move `key` through absolute (d, w) points `via` to `end`, as a block, pull, path, run, rush or route."""
    start = np.array(p._cur(key))
    pts = [tuple(np.array(q) - start) for q in (*via, end)]
    if kind == "block":
        return p.block(key, pts=pts, delay=delay, speed=speed)
    if kind == "pull":
        return p.pull(key, pts, delay=delay, speed=speed)
    if kind == "rush":
        return p.rush(key, pts=pts, delay=delay, speed=speed)
    if kind == "route":
        return p.route(key, [tuple(q) for q in (*via, end)], absolute=True, delay=delay, speed=speed)
    return {"path": p.path, "run": p.run}[kind](key, pts, delay=delay, speed=speed)


def pass_pro(p, keys=OL_KEYS, depth=-1.2):
    for k in keys:
        p.block(k, pts=[(depth + 0.7, 0.0)])


def _P(fld, p, d, w):
    return fld.xy(*p.to_abs(d, w))


def say(fld, p, d, w, text, color=style.INK_2, fontsize=7.2, ha="center", weight="normal", alpha=0.92):
    """A short on-field label at (d, w)."""
    x, y = _P(fld, p, d, w)
    fld.ax.text(float(x), float(y), text, fontsize=fontsize, color=color, ha=ha, va="center", zorder=9,
                fontweight=weight, clip_on=True,
                bbox=dict(boxstyle="round,pad=0.18", fc=style.SURFACE, ec="none", alpha=alpha))


def point_to(fld, p, text_at, target, text, color=style.INK_2, fontsize=7, shrink=7):
    """A label with a thin leader line to the player or spot it names."""
    (xt, yt), (xp, yp) = _P(fld, p, *text_at), _P(fld, p, *target)
    fld.ax.annotate(text, (float(xp), float(yp)), xytext=(float(xt), float(yt)), fontsize=fontsize,
                    color=color, ha="center", va="center", zorder=9,
                    arrowprops=dict(arrowstyle="-", color=color, lw=0.7, shrinkA=2, shrinkB=shrink),
                    bbox=dict(boxstyle="round,pad=0.18", fc=style.SURFACE, ec="none", alpha=0.92))


def num(fld, p, d, w, n, color=GREEN):
    """A numbered progression circle."""
    x, y = (float(v) for v in _P(fld, p, d, w))
    fld.ax.add_patch(Circle((x, y), 0.85, fc="white", ec=color, lw=1.4, zorder=10))
    fld.ax.text(x, y, str(n), ha="center", va="center", fontsize=7, color=color, fontweight="bold", zorder=11)


def clean(ax, numbers=True):
    """Drop hash ticks (and optionally yard numbers); clip text to the window."""
    for ln in list(ax.lines):
        xs, ys = ln.get_data()
        if len(xs) == 2 and ln.get_linewidth() == 0.5 and np.hypot(xs[1] - xs[0], ys[1] - ys[0]) < 1.0:
            ln.remove()
    for t in list(ax.texts):
        if numbers and t.get_text().isdigit() and t.get_alpha() == 0.55:
            t.remove()
        else:
            t.set_clip_on(True)


def draw(p, width_in=6.2, ax=None, numbers=True, **kw):
    """Play.draw, sized for the page, cleaned for a zoomed window."""
    fld = p.draw(width_in=width_in, ax=ax, **kw)
    clean(fld.ax, numbers=numbers)
    return fld


def ruler(fld, p, depths=(5, 10, 15), fs=6.5):
    """Depth labels (yards past the line of scrimmage) along the left edge of the window."""
    x_left = (160 / 3) - fld.y1 + 0.4
    for d in depths:
        y = p.los + d
        if fld.x0 + 0.8 <= y <= fld.x1 - 0.8:
            fld.ax.text(x_left, y, f"{d}" + (" yd" if d == depths[0] else ""), fontsize=fs, color=style.INK_2,
                        ha="left", va="center", zorder=12,
                        bbox=dict(boxstyle="round,pad=0.12", fc=style.SURFACE, ec="none", alpha=0.85))


def title(ax, text, size=8.6):
    ax.set_title(text, fontsize=size, loc="left", color=style.INK, fontweight="bold")


# --- frame strips (PDF) and video (web) for simulated plays ------------------------------------------
MARK_R = 0.62


def tracking(play):
    df = play.to_tracking()
    df["_id"] = df["displayName"].astype(str)
    return df


def catch_time(df, key, t_throw):
    """First tracking time after the throw when the ball reaches `key` (the simulated catch)."""
    b = df[df.side == "ball"].set_index("time")[["x", "y"]]
    q = df[df.displayName == key].set_index("time")[["x", "y"]]
    gap = ((b - q) ** 2).sum(axis=1) ** 0.5
    gap = gap[gap.index > t_throw + 0.05]
    return float(gap[gap < 0.6].index[0])


def panel(ax, play, df, t, lateral, window, rings=(), ring_r=0.95, mark_r=None, trails=None):
    """One moment of a simulated play on `ax`: markers, trails (only for the keys in `trails`, if given), rings."""
    tmap = df.groupby("frameId")["time"].first()
    fid = int((tmap - t).abs().idxmin())
    ylo, yhi = sorted((play.ball_y - lateral[0], play.ball_y - lateral[1]))
    fld = Field(ax=ax, orient="vertical", los=play.los, window=window, numbers=False, hashes=False,
                lateral=(ylo, yhi), to_go=play.to_go)
    cur = df[df["frameId"] == fid]
    shown = cur[cur["x"].between(play.los + window[0] + 0.5, play.los + window[1] - 0.3)
                & cur["y"].between(ylo + 0.5, yhi - 0.5) | (cur["side"] == "ball")]
    ids = set(shown["_id"])
    keep = ids if trails is None else ids & set(trails)
    tr = df[(df["frameId"] <= fid) & df["_id"].isin(keep) & (df["side"] != "ball")]
    for (_, side), g in tr.groupby(["_id", "side"]):
        xs, ys = fld.xy(g["x"].values, g["y"].values)
        ax.plot(xs, ys, color=style.OFFENSE if side == "off" else style.DEFENSE, lw=0.9, alpha=0.4, zorder=3)
    ppy = fld.pts_per_yard()
    mr = mark_r or MARK_R
    for _, r_ in shown.iterrows():
        x, y = (float(v) for v in fld.xy(r_["x"], r_["y"]))
        if r_["side"] == "ball":
            ax.add_patch(Ellipse((x, y + 0.45), 0.75, 0.45, fc=style.BALL, ec="white", lw=0.5, zorder=12))
            continue
        lab = str(r_.get("label", "") or "")
        if r_["side"] == "off":
            ax.add_patch(Circle((x, y), mr, fc=style.OFFENSE, ec="white", lw=0.6, zorder=8))
            tc = "white"
        else:
            ax.add_patch(RegularPolygon((x, y), 4, radius=mr * 1.3, orientation=np.pi / 4,
                                        fc=style.DEFENSE_TINT, ec=style.DEFENSE, lw=1.0, zorder=8))
            tc = style.INK
        fs = max(4.0, min(8.0, ppy * mr / MARK_R * (0.85 if len(lab) <= 1 else 0.6)))
        ax.text(x, y, lab, ha="center", va="center", fontsize=fs, color=tc, fontweight="bold", zorder=10)
    for key, col in rings:
        r_ = cur[cur["displayName"] == key].iloc[0]
        x, y = fld.xy(r_["x"], r_["y"])
        ax.add_patch(Circle((float(x), float(y)), ring_r, fc="none", ec=col, lw=1.6, zorder=7))
    for patch in ax.patches:
        patch.set_clip_on(True)
    return fld


def strip(moments, lateral, window, notes=None, rings=(), ncols=2, width_in=6.2, after=None, mark_r=None,
          trails=None, note_h=0.8):
    """Numbered frame strip. `moments` is a list of (play, t, tag)."""
    cache = {}
    nrows = int(np.ceil(len(moments) / ncols))
    pw = width_in / ncols
    span_w, span_d = lateral[1] - lateral[0], window[1] - window[0]
    fig, axes = plt.subplots(nrows, ncols, squeeze=False,
                             figsize=(width_in, nrows * (pw * span_d / span_w + (note_h if notes else 0.25))))
    for ax in axes.flat[len(moments):]:
        ax.axis("off")
    for i, (ax, (play, t, tag)) in enumerate(zip(axes.flat, moments)):
        if id(play) not in cache:
            cache[id(play)] = tracking(play)
        fld = panel(ax, play, cache[id(play)], t, lateral, window, rings, mark_r=mark_r, trails=trails)
        if after:
            after(fld, play, t, i)
        lab = "snap" if abs(t) < 1e-6 else f"{t:+.1f} s"
        ax.set_title(f"{i + 1}.  {lab}" + (f"   {tag}" if tag else ""), fontsize=8.3, color=style.INK_2, loc="left")
        if notes:
            ax.text(0.5, -0.03, notes[i], transform=ax.transAxes, ha="center", va="top",
                    fontsize=7.1 if ncols < 3 else 6.6, color=style.INK)
    fig.tight_layout(h_pad=1.3 if notes else 0.6, w_pad=0.6)
    return fig


def video(play, notes, lateral, window, rings=(), width_in=4.8, after=None, mark_r=None, trails=None):
    """Web version: the play frame by frame, with each moment's note shown as the caption."""
    import shutil
    from matplotlib import animation
    from IPython.display import HTML, display
    span_w, span_d = lateral[1] - lateral[0], window[1] - window[0]
    fig, ax = plt.subplots(figsize=(width_in, width_in * span_d / span_w + 0.5))
    df = tracking(play)
    tmap = df.groupby("frameId")["time"].first()
    frames = [float(tmap[f]) for f in sorted(df["frameId"].unique())]

    def update(k):
        t = frames[k]
        ax.clear()
        fld = panel(ax, play, df, t, lateral, window, rings, mark_r=mark_r, trails=trails)
        if after:
            after(fld, play, t, -1)
        cur = [n for tt, n in notes if tt <= t + 1e-6]
        ax.set_title(("snap" if abs(t) < 0.05 else f"{t:+.1f} s") + (("\n" + cur[-1].replace("\n", " ")) if cur else ""),
                     fontsize=8.2, color=style.INK, loc="left")
        return []

    anim = animation.FuncAnimation(fig, update, frames=range(len(frames)), interval=100)
    html = (anim.to_html5_video(embed_limit=40).replace("<video ", '<video style="max-width:100%;height:auto" ')
            if shutil.which("ffmpeg") else anim.to_jshtml(default_mode="once"))
    plt.close(fig)
    display(HTML(html))


def animation_or_strip(play, moments, lateral, window, rings=(), after=None, ncols=2, width_in=6.2, mark_r=None,
                       trails=None, note_h=0.8):
    """Video on the website; a numbered, captioned frame strip in the PDF. moments = [(t, tag, note)]."""
    if pdf_build():
        strip([(play, t, tag) for t, tag, _ in moments], lateral, window, notes=[n for *_, n in moments],
              rings=rings, ncols=ncols, width_in=width_in, after=after, mark_r=mark_r, trails=trails, note_h=note_h)
        plt.show()
        return
    video(play, [(t, n) for t, _, n in moments], lateral, window, rings=rings, after=after, mark_r=mark_r,
          trails=trails)
Show the Python (the data: early-down runs and passes, 2023-2025)
# Every early-down snap in a competitive game, 2023-2025 regular seasons, from nflverse play-by-play.
from gridiron.data import pbp

COLS = ["season", "season_type", "posteam", "down", "qtr", "wp", "play_type", "air_yards", "complete_pass",
        "yards_after_catch", "receiving_yards", "yards_gained", "epa", "success", "qb_spike", "qb_kneel",
        "qb_scramble", "qb_dropback", "sack"]
raw = pbp([2023, 2024, 2025], columns=COLS)
ED = raw[(raw.season_type == "REG") & raw.down.isin([1, 2]) & raw.wp.between(0.10, 0.90)
         & (raw.qb_spike != 1) & (raw.qb_kneel != 1)]
RUNS = ED[(ED.play_type == "run") & (ED.qb_scramble != 1)]
PASSES = ED[(ED.play_type == "pass") & ED.air_yards.notna()]          # thrown balls (sacks have no air yards)
DROPBACKS = ED[ED.qb_dropback == 1]                                    # passes + sacks + scrambles
BINS = [(-99, 0, "Pass, thrown behind\nthe line (screens, swings)"), (0, 5, "Pass, 1–5 yards\nin the air"),
        (5, 10, "Pass, 6–10 yards"), (10, 15, "Pass, 11–15 yards"), (15, 99, "Pass, 16+ yards")]


def summary(df):
    return pd.Series({"n": len(df), "sr": df.success.mean(), "epa": df.epa.mean(), "yds": df.yards_gained.mean()})


ROWS = [("Designed run", summary(RUNS))]
for lo, hi, lab in BINS:
    ROWS.append((lab, summary(PASSES[(PASSES.air_yards > lo) & (PASSES.air_yards <= hi)])))
ROWS.append(("Every dropback\n(sacks and scrambles in)", summary(DROPBACKS)))
TAB = pd.DataFrame({lab: s for lab, s in ROWS}).T
N_PASS = len(PASSES)
SHORT = PASSES[PASSES.air_yards <= 5]
SHORT_SHARE = len(SHORT) / N_PASS
BEHIND_SHARE = (PASSES.air_yards <= 0).mean()
RUN_SR, RUN_YDS = TAB.loc["Designed run", "sr"], TAB.loc["Designed run", "yds"]
S15 = TAB.iloc[2]
SCR = TAB.iloc[1]
_c15 = PASSES[(PASSES.air_yards > 0) & (PASSES.air_yards <= 5) & (PASSES.complete_pass == 1)]
YAC_SHORT = _c15.yards_after_catch.sum() / _c15.receiving_yards.sum()

# the 2025 census benchmark: early-down throws of 5 air yards or less, by team
P25 = PASSES[PASSES.season == 2025]
TEAM25 = P25.groupby("posteam").air_yards.apply(lambda s: (s <= 5).mean()).sort_values()
LG25 = (P25.air_yards <= 5).mean()
# do the Walsh-tree teams of 2025 (their head coaches are in the tree figure) throw short more than the rest?
TREE25 = ["KC", "SF", "LA", "GB", "MIN", "CIN", "JAX", "BAL", "MIA", "CLE"]
TREE_SH = P25[P25.posteam.isin(TREE25)].air_yards.le(5).mean()
REST_SH = P25[~P25.posteam.isin(TREE25)].air_yards.le(5).mean()
JAX_RANK = int(TEAM25.rank(ascending=False)["JAX"])


def ordinal(n):
    return f"{n}{'th' if 10 <= n % 100 <= 20 else {1: 'st', 2: 'nd', 3: 'rd'}.get(n % 10, 'th')}"

Third-and-3 at the Dallas 6, fifty-eight seconds left in the 1981 NFC Championship Game, the 49ers behind 27–21. Joe Montana takes the snap and sprints to his right with three Cowboys chasing him toward the sideline. His first receiver has slipped. Montana pump-fakes, backpedals, and throws the ball high toward the back of the end zone, so high that the Cowboys’ defensive end Ed “Too Tall” Jones, who had left his feet on the fake, can’t reach it. Dwight Clark jumps and gets his fingertips to it. It’s the most famous catch in the sport, and it gets called a miracle, a desperation heave, a throwaway that a receiver happened to catch. Clark always said it was none of those things: it was a play from the book, run on the practice field so many times that he knew where the ball would be.1 Which was it? And how did a team that had gone 2–14 two years earlier end up in that spot at all? The answers are the same answer, and it’s the subject of this chapter.

You’ll be able to:

NoteYou’ll need

This is a Level 4 case study, and it builds on two chapters in particular. One line each:

Terms from elsewhere you’ll meet often: the West Coast offense (WCO) and its long play calls, from Offensive Systems; slant, flat route, timing route, progression, YAC (yards after the catch) and the horizontal stretch, from Passing Fundamentals; slant-flat and the rhythm throw, from The Quick Game; the pro set, also called split backs, from Classic Formations; check-release, from Pass Protection; Cover 3, from The Cover 3 Family; the opening script, from The Game Plan; and EPA and success rate, from Downs, Distance, and Field Position.

Reading the diagrams: offense is blue circles (Q quarterback; H the halfback and F the fullback in split backs; X, Y, Z the receivers, Y the tight end; T G C G T the line). Defense is outlined squares (E end, T tackle, S M W the Sam, Mike and Will linebackers, CB corners, FS and SS safeties). Solid arrows are routes and runs, a thick dark arrow is the ball carrier, dashed orange arrows are defenders’ drops, a bar at the end of a line is a block, and the dashed brown line is the pass. A green ring marks the player to watch; numbered green circles are the quarterback’s progression. The left-edge numbers give depth in yards past the line of scrimmage. Plays are illustrative, typical of the era, not traced from film, unless a caption says otherwise.

The problem: 2–14, and no way to run the ball

In January 1979 the San Francisco 49ers hired a 47-year-old Stanford coach to fix a franchise that had just gone 2–14. Bill Walsh had waited a long time for the job. He had coached in pro football since 1966, with the Raiders (then an AFL team) and then for eight seasons with Paul Brown’s Cincinnati Bengals, where he ran the passing game. When Brown retired from coaching after the 1975 season he picked another assistant, Bill “Tiger” Johnson, as his successor; Walsh resigned, and later said Brown had worked against him when other teams asked about him for head-coaching jobs. He spent 1976 as the Chargers’ offensive coordinator, coaching a young Dan Fouts, and two seasons winning at Stanford before the 49ers called. He was also the general manager, which mattered: the man drawing the plays was the man choosing the players.2

What he inherited was a team with no good answer to the oldest question in football: how do you keep the ball? In the 1970s the answer was the run. A team that could gain four yards on first down and four more on second down controlled the clock, the field and the game, and passing was what you did when that failed. Walsh’s 49ers couldn’t do it. They didn’t have the linemen to push other teams around, and Walsh had been in exactly this spot before. Of the young expansion Bengals he had coached in the late 1960s, he said: “We couldn’t control the football with the run; teams were just too strong. So it had to be the forward pass, and obviously it had to be a high-percentage, short, controlled passing game.”3

That sentence is the whole chapter in miniature. Walsh didn’t decide to pass more instead of running. He decided that the pass could be his running game, doing the same job (four yards at a time, the clock moving, the ball kept) with players and plays he could actually win with. He had already proved it once, with Virgil Carter in Cincinnati (the story is in The Quick Game); San Francisco let him build a whole team around it, just after the 1978 rules had made it far easier.

You’ve met the outline of this story in 1978 and the Passing Revolution: the hire, Montana in the third round of the 1979 draft (with Dwight Clark in the tenth), 2–14 and then 6–10, and Super Bowl XVI two years later.4 This chapter is the version with the clock running: what the plays looked like snap by snap, why each piece was there, and what happened to the system after its inventor left.

The scheme answer: the pass as a run

Here is the idea in one term. A horizontal passing game is an offense built on short, timed throws toward the sidelines and across the field, caught a few yards past the line of scrimmage and turned into gains by the run after the catch. Its throws are so short that they’re often described as extended handoffs.5 The name contrasts it with the vertical passing of Don Coryell’s Chargers, which attacked down the field.

What does a run actually do for an offense? Three things. It gains a few yards with little risk of losing many. It puts the ball in a fast player’s hands while he’s moving forward, so he can make a defender miss. And it attacks the defense at a spot the offense chose. A short pass to the flat does all three, and Figure 59.1 shows the comparison that Walsh’s offense made every week: two ways to get the halfback to the right edge with the ball.

Show the Python
fig, axes = plt.subplots(1, 2, figsize=(6.6, 4.3))
LAT, WIN = (-5.0, 21.5), (-8.0, 15.5)

# ---------------- left: toss sweep right
p = Play(split_backs(z_w=13.5, h_side=1), era_43("one"), los=35, to_go=10)
go_to(p, "HB", (3.0, 11.8), kind="run", via=[(-4.4, 5.6), (-2.0, 10.0)], speed=6.5)  # the halfback takes the toss
p.pitch("HB", t=0.45)
go_to(p, "FB", (-1.4, -4.4), kind="block", speed=4.0)                      # the fullback cuts off the backside end
go_to(p, "RT", (0.5, 4.6), kind="block")                                   # reach the end
go_to(p, "Y", (2.2, 7.0), kind="block")                                    # reach the Sam
go_to(p, "RG", (2.0, 8.8), kind="pull", via=[(-1.6, 2.4), (-1.0, 6.6)])     # pull around Y, lead on the force
for k in ("LT", "LG", "C"):
    p.block(k, pts=[(0.6, 0.6)])
go_to(p, "SAM", (2.6, 7.6), kind="path", speed=4.5)
go_to(p, "MIKE", (2.4, 6.0), kind="path", speed=4.5)                       # flows over the top, unblocked
go_to(p, "SS", (3.2, 10.4), kind="path", speed=4.5)
go_to(p, "Z", (4.6, 14.6), kind="block")                                     # Z blocks the corner
p.highlight("HB")
p.highlight("RT", "Y", "RG", "Z", color=style.INK_2)
fld = draw(p, ax=axes[0], lateral=LAT, window=WIN, title=False)
title(axes[0], "A toss sweep: four blocks to win the edge")
say(fld, p, 9.8, 15.6, "the defense\nflows to it", color=style.DEFENSE_DARK, fontsize=6.8)
point_to(fld, p, (6.0, 1.4), (2.4, 6.0), "M: unblocked", color=style.DEFENSE_DARK, fontsize=6.6)
say(fld, p, -6.6, 13.4, "grey rings: 4 blockers\nwho must all win", color=style.OFFENSE_DARK, fontsize=6.6)

# ---------------- right: the flat pass
dfn = [q.moved(w=15.0) if q.key == "RCB" else q.moved(d=7.0, w=9.0) if q.key == "SS" else q for q in era_43("one")]
p = Play(split_backs(z_w=15.0, h_side=1), dfn, los=35, to_go=10)
p.dropback(3.2)
pass_pro(p)
p.block("FB", pts=[(0.3, -0.5)])
go_to(p, "HB", (0.8, 10.8), kind="route", via=[(-3.2, 6.2)])
go_to(p, "HB", (6.4, 12.2), kind="run", speed=6.2)
p.route("Y", r.curl(10))
p.route("Z", r.go(12))
p.drop("SS", (9.0, 6.2), speed=4.2)                                        # sinks under the curl...
p.drop("RCB", (13.0, 15.5))
go_to(p, "SAM", (4.6, 5.6), kind="path", speed=3.8)
p.pass_to("HB", t=1.0)
p.highlight("HB")
fld = draw(p, ax=axes[1], lateral=LAT, window=WIN, title=False)
title(axes[1], "A flat pass: the ball outruns the pursuit")
say(fld, p, -6.6, 9.2, "3-step drop:\nball out in ~1 s", color=style.INK_2, fontsize=6.8)
point_to(fld, p, (-3.4, 12.8), (3.4, 11.5), "run after\nthe catch", color=style.OFFENSE_DARK, fontsize=6.6)
point_to(fld, p, (12.6, 9.6), (9.0, 6.2), "SS: curl or flat?\nHe sinks", color=style.DEFENSE_DARK, fontsize=6.6)
plt.tight_layout(w_pad=0.6)
plt.show()
Figure 59.1: The same job, two ways (illustrative, 1980s 4-3, Cover 3). Left: a toss sweep. The halfback (H, green ring) needs four blockers (grey rings) to win at once: the right tackle and tight end reach their men, the right guard pulls around the tight end to lead on the strong safety, and the flanker (Z) blocks the cornerback. Even then the Mike linebacker, flowing over the top unblocked, can make the tackle; one lost block and it’s a loss. Right: the halfback runs a flat route instead and the quarterback throws it on a three-step drop, so the ball arrives about a second and a half after the snap, near the sideline. The strong safety (SS) is the one defender responsible for both the tight end’s curl and the flat, and he can’t be in both places. Here he sinks under the curl, so the flat is the throw; if he had widened to the flat, the curl behind him would be. No one has to win a block at the edge. That is the horizontal passing game: a pass that does a run’s job.

Look at what the pass removes. The toss sweep is a bet that four blockers each win their fight in the same second; the flat pass is a bet that one throw is accurate. Against a defense that’s bigger and stronger than your line, the second bet is the better one, and in 1979 the 49ers’ line was the weak point. The pass also changes who does the tackling. On a run the first defenders to arrive are linemen and linebackers, the biggest men on the field. On a quick throw to the edge the tackler is usually a defensive back, 30 or 40 pounds lighter, and often backing up when the ball arrives.

Then there is the horizontal stretch, which is what makes the throw hard to take away. You can see it in the right panel: in this Cover 3 the strong safety is the curl-flat defender, responsible for both the tight end’s curl and the flat, and two receivers are in his area at once. Whichever he takes, the quarterback throws the other. A zone defender who covers the flat can’t also cover a route breaking inside him, so the offense puts a receiver on each side of him and throws to whichever he leaves. Spread that across the field (the backs releasing to both flats, receivers crossing underneath) and every short defender has two receivers to worry about. The defense can’t add enough players underneath to cover them all without emptying the deep part of the field. That, Walsh argued, was when the long throw came open.

ImportantWhy it exists, and what it costs

The horizontal passing game exists because a team that can’t win at the line of scrimmage can still win in space. It trades the run’s physical contest for a contest of precision and speed: the ball gets to the edge before the defense does, and the tackle is made, or missed, by a smaller man in open field.

The bill comes in four parts. It needs an accurate quarterback, more than a strong-armed one: a throw a yard behind the receiver turns a five-yard gain into a tackle at the catch. It needs receivers who run after the catch, because the throw itself only travels a few yards. A short throw can still be intercepted, and a linebacker who reads it and jumps it has a clear run the other way. And it gets squeezed: once a defense stops worrying about the deep ball, its underneath defenders can crowd the short throws, which is why Walsh insisted the offense keep threatening deep. A West Coast team that only throws short isn’t running Walsh’s offense.

CautionCommon misconception

“The West Coast offense is dink-and-dunk.” Walsh hated the word finesse, which writers attached to his offense, and the record supports him.6 The short throws were the setup, not the point: they pulled linebackers and safeties forward, and the 49ers then threw over them. Jerry Rice caught 22 touchdown passes in twelve games in 1987, many of them long runs after slants and deep throws, which is not what dink-and-dunk looks like.7 What is true is that the offense was built so that a short gain was an acceptable result, which most 1970s passing games couldn’t say.

And the running game came back. “Couldn’t run” describes the 49ers Walsh inherited, not the team he built. In 1979 they ran on 44% of their plays, against 54% for the league as a whole, and threw more passes than any other team. By 1981 they ran on 52% (league: 51%), and in 1984 on 52% again (league: 49%), when Wendell Tyler set a team record with 1,262 rushing yards.8 The short passing game didn’t replace the run; it made the run possible again. Linebackers who had to respect two backs releasing into routes, and safeties pulled forward by short throws, left fewer defenders to stop a handoff. The run in turn gave the 49ers their play-action passes: a fake to a back from split backs, with the quarterback under center and his back to the defense, froze linebackers who had been burned by both. That under-center play-action is the bridge to Mike Shanahan’s offense, later in this chapter.

Design goal 1: timing

Walsh’s offense is a timing offense, the purest version of the idea you met in Passing Fundamentals: the quarterback’s drop and the receiver’s route are designed as one unit, so that the ball leaves his hand at the moment the receiver breaks, and arrives before the defender can react. That chapter’s chart of drops and route depths is the West Coast offense’s skeleton: a three-step drop for quick routes caught about 5 to 7 yards downfield (a slant breaks after three hard steps, about 4 yards, and is caught at 6 or 7), a five-step drop with a hitch for routes at 10 to 14, a seven-step drop for the deep comebacks and posts.

On a broadcast replay, a timed throw has a look you can learn. The ball leaves the quarterback’s hand before the receiver has finished his break, often before the receiver has turned his head, and the receiver catches it without slowing down. A throw that waits until the receiver is visibly open, and standing still, is a late throw, however good the result.

Two things made Walsh’s version different from a generic timing game.

The quarterback’s feet were his reading clock. Each step of the drop meant a receiver in the progression. On the last step of a quick drop he threw to the first receiver if he was open. If not, the hitch step that followed was his permission to go to the second, and so on down to the checkdown. This is why the offense could be taught: a quarterback didn’t have to decide when to give up on a receiver, because his feet decided for him.

The backs were receivers, not spare blockers. The original West Coast offense lined up in split backs: one back behind each tackle, a tight end, and two wide receivers.9 On a pass play both backs had rules that turned them into receivers. Each checked his blocking assignment first (a linebacker who might blitz) and released into a short route if nobody came, which is the check-release. A defense that covered all five possible receivers had to account for both backs, and a defense that blitzed found a back already in its way.

When the blitz brought more rushers than the backs and line could block, the answer was the same as in any dropback offense: a hot route, a receiver (often the tight end or the back on the blitz side) who knows to look for the ball immediately, and a sight adjustment by a wide receiver who converts his route into a quick slant when he sees the blitz. The quick throw goes where the blitzer came from. (The protection side of this is in Pass Protection.)

The 1978 chapter drew the slant side of Walsh’s most basic read: the Will linebacker widens to the flat, and the ball goes to the slant inside him. Figure 59.2 is the other half of the same read, with the clock running: the slant-flat from split backs, the Will sinking under the slant, and the ball going to the halfback in the flat.

Show the Python
sf = Play(split_backs(), era_43("one"), los=35, to_go=10)
sf.dropback(3.2)
pass_pro(sf)
sf.block("FB", pts=[(0.4, -0.6)])
sf.route("X", [(0, 0), (4.0, 0), (8.4, -5.6)], speed=6.8)                     # slant: 3 hard steps, then break
go_to(sf, "HB", (3.0, -13.0), kind="route", via=[(-3.4, -6.0), (-0.4, -10.0)], delay=0.3,
      speed=7.0)                                                               # check for a blitzer, then the flat
go_to(sf, "HB", (8.8, -16.0), kind="run", speed=6.4)                         # run after the catch
sf.route("Y", r.curl(10))
sf.route("Z", r.go(10))
go_to(sf, "WILL", (5.4, -5.0), kind="path", speed=4.3)                      # Will sinks under the slant
go_to(sf, "LCB", (11.4, -14.2), kind="path", speed=4.0)                      # corner bails, deep third
go_to(sf, "LCB", (10.6, -14.6), kind="path", delay=0.15, speed=3.0)          # then breaks down
go_to(sf, "MIKE", (7.2, -1.0), kind="path", speed=4.0)
go_to(sf, "FS", (16.0, 0.0), kind="path", speed=3.0)
sf.pass_to("HB", t=1.3)
SF_CATCH = catch_time(tracking(sf), "HB", 1.3)

SF_NOTES = ["Split backs, three-step drop.\nWill (W) is the one man between\nthe slant (X) and the flat (H).",
            "Third step lands, about 1.3 s. W\nsinks under the slant, so the ball\ngoes to H, who isn't there yet.",
            "H catches it in stride near\nthe numbers, already running,\nabout three yards past the line.",
            "Run after the catch: five or\nsix more yards before the\ncorner, who bailed deep, arrives."]
SF_MOMENTS = [(0.0, "", SF_NOTES[0]), (1.3, "the throw", SF_NOTES[1]),
              (round(SF_CATCH, 1), "the catch", SF_NOTES[2]), (round(SF_CATCH, 1) + 1.0, "", SF_NOTES[3])]


def sf_after(fld, p, t, i):
    if i == 0:
        say(fld, p, 8.2, -10.8, "slant?", color=style.OFFENSE_DARK, fontsize=6.8, weight="bold")
        say(fld, p, 2.4, -15.6, "flat?", color=style.OFFENSE_DARK, fontsize=6.8, weight="bold")


animation_or_strip(sf, SF_MOMENTS, lateral=(-19.5, 6.0), window=(-7.5, 15.0),
                   rings=(("HB", GREEN), ("WILL", GREEN)), after=sf_after)
Figure 59.2: Slant-flat from split backs, with the halfback in the flat (simulated, BDB-format tracking; illustrative 1980s 4-3, Cover 3). The quarterback takes three steps from under center. X runs a slant, three hard steps and then the break inside; the halfback (H, ringed) checks for a blitzer, then releases to the flat. The Will (W, ringed) is the one defender between them, and here he sinks inside under the slant, so the throw goes to H. It leaves the quarterback’s hand about 1.3 seconds after the snap, before H has reached the flat. H catches it on the run near the numbers, about three yards past the line, a little over two seconds after the snap, and turns up the sideline for five or six more yards before the cornerback, who had backed up to guard his deep third, can come back down. Faint lines are where each player has been; the brown dot is the ball.

Notice when the ball leaves: on the third step, before the halfback has even reached the flat. The quarterback isn’t waiting to see the halfback open; he’s reading one defender, the Will, and throwing to a spot the halfback will reach. That’s what timing buys. The defense gets no time to react to the throw, because the throw is already in the air when the receiver arrives. And it’s what timing costs: if the halfback is a step late because a linebacker bumped him, or the quarterback’s third step lands a beat slow, the whole thing falls apart. (The same concept from a modern shotgun spread, with a slot receiver instead of a back in the flat, is in The Quick Game.)

Slant-flat is the play everyone remembers, but most of the 49ers’ passing yards came from deeper: the five-step drop, thrown on time to receivers crossing at 8 to 15 yards, with a back underneath as the safety valve. The West Coast version of drive in Figure 59.3 shows the shape: a shallow cross under a dig from the same side, a high-low read of the middle linebacker, and both backs as the last two reads, one on each side.

Show the Python
dfn = [q.moved(w=14.5) if q.key == "RCB" else q for q in era_43("one")]     # outside leverage on Z
p = Play(split_backs(), dfn, los=35, to_go=10)
p.dropback(5.0)
pass_pro(p)
go_to(p, "Y", (4.0, -11.0), kind="route", via=[(2.2, 3.6), (3.4, -2.0)])          # shallow cross
go_to(p, "Z", (12.0, -4.0), kind="route", via=[(12.0, 12.8)])                     # 12-yard dig
p.route("X", r.go(14))
go_to(p, "HB", (-0.6, -8.4), kind="route", via=[(-3.8, -6.0)], delay=0.5)         # check, then swing (behind the line)
go_to(p, "FB", (1.6, 9.6), kind="route", via=[(-3.6, 5.4)], delay=0.5)            # check, then the flat
p.drop("MIKE", (7.6, 0.8))
p.drop("WILL", (6.6, -7.0))
p.drop("SAM", (6.6, 7.8))
p.drop("SS", (7.6, 11.6))
p.drop("LCB", (15.5, -13.0))
p.drop("RCB", (15.5, 14.0))
p.drop("FS", (16.0, 0.0))
p.highlight("MIKE")
fld = draw(p, width_in=6.2, lateral=(-21.0, 18.0), window=(-8.5, 18.5), title=False)
ruler(fld, p, depths=(5, 10, 15))
num(fld, p, 12.0, -1.6, 1)
num(fld, p, 2.4, -10.6, 2)
num(fld, p, -2.4, -9.6, 3)
num(fld, p, 0.4, 11.2, 4)
say(fld, p, 13.4, 9.0, "Z: dig at 12", color=style.OFFENSE_DARK, fontsize=7, weight="bold")
say(fld, p, 2.1, -6.6, "Y: shallow", color=style.OFFENSE_DARK, fontsize=7, weight="bold")
point_to(fld, p, (10.6, 4.0), (7.6, 0.8), "M: up or back?", color=style.DEFENSE_DARK, fontsize=6.8)
say(fld, p, -7.2, 5.2, "5-step drop", fontsize=6.8)
plt.show()
Figure 59.3: Drive from split backs, West Coast style (illustrative, 1980s 4-3, Cover 3). The tight end (Y) runs a shallow cross at about 3 to 4 yards; the flanker (Z) runs a 12-yard dig behind him on the same line. The Mike (M, ringed) can come up for the shallow or drop under the dig, not both, and the quarterback reads him on a five-step drop, high to low: (1) the dig, timed to the top of the drop, if the Mike comes up; (2) the shallow, still crossing a beat later, if he drops. Both backs check for a blitzer first: the halfback then swings left behind the line (3), the fullback releases to the right flat (4). X’s go route takes the left cornerback deep, and the free safety has the deep middle. Every receiver is running when he catches the ball.

Compare it with the plate in Dropback Concepts, drawn from a modern spread, and you’ll notice two differences. The first is the read order. That chapter reads drive low to high, shallow first, because against man coverage the shallow is open early or not at all. Walsh’s version is read high to low, because of the drop. On a five-step drop with a hitch, the dig is timed to arrive just as the quarterback finishes his drop and hitches up, so it gets the first look; the shallow keeps running across the field and is still available on the next hitch; the backs come last. Dig on time, shallow late, back last. Both orders work, because the Mike still can’t take both routes; what changes is which one the timing favours.

The second difference is where the outlets come from. In the spread they’re receivers; in Walsh’s version they’re the two backs, who were also the offense’s protection a second earlier. That double duty is the West Coast offense in one picture: the same eleven players doing two jobs, so the defense has to cover more men than it expected.

Design goal 2: yards after the catch

Every one of these throws asks the receiver to keep running after he catches it. That’s not a happy accident; it’s what the routes were built for. The slant catches the receiver moving across the field at full speed. The shallow cross catches him running away from the defender who started across from him. The flat route catches the back moving toward the sideline with the cornerback backpedaling away from him, at least against Cover 3, where the corner is bailing to his deep third. (Against Cover 2 the corner squats in the flat, so the flat throw is the one being taken away, and the West Coast answer was to throw behind him instead: the curl, or a corner route into the hole between the corner and his safety.) In each case the receiver is already in motion when the ball arrives, and the nearest defenders are either behind him or moving the wrong way.

A run after the catch is worth more than it looks. On the slant in particular, the receiver catches the ball in the gap between the underneath zone and the deep zone, the only defenders who can tackle him are a step behind, and one missed tackle can turn seven yards into seventy. That’s how the 49ers’ short offense produced long touchdowns.

It also explains the 49ers’ roster. Walsh’s best players were the ones who could turn a short catch into a long gain:

  • Jerry Rice, a first-round pick in 1985 out of Mississippi Valley State, who finished his career with more catches, receiving yards and touchdowns than anyone in NFL history (1,549, 22,895 and 208 total touchdowns).10 His signature was the slant, caught in stride and taken the distance.
  • Roger Craig, a second-round pick in 1983, a running back who was also one of the best receivers on the team. In 1985 he became the first player to run for 1,000 yards and catch passes for 1,000 more in the same season, and he led the league with 92 receptions.11 A running back leading the NFL in catches is the West Coast offense’s fingerprint in a single statistic. Many of those catches came on the back’s option route: he runs straight at the linebacker covering him, reads which way the linebacker leans, and breaks the other way, in or out. The linebacker has to commit before the back does, and against a back as quick as Craig a wrong guess is a catch in open field.
  • Dwight Clark and Freddie Solomon, who did most of the catching in the early 1980s, and later John Taylor, Rice’s partner on the other side.

The yards after the catch also mean the offense isn’t at the mercy of how far the quarterback can throw. Montana, a third-round pick, wasn’t known for arm strength; nor, before him, was Virgil Carter. What both could do was put the ball in the right place at the right time, which leads to the third design goal. (YAC as a statistic, and the modern version that measures it against what a typical receiver would have gained, YAC over expected, belong to the Shanahan case study, which is where the 49ers’ current offense picks this thread back up.)

Design goal 3: ball placement

If the receiver has to keep running, where the ball arrives matters as much as whether it arrives. A pass thrown to the receiver’s back hip makes him stop, turn and reach behind him, and by the time he’s turned upfield again the defender is on him. The same pass thrown a foot in front of him, at chest height on his upfield shoulder, lets him catch it without breaking stride. Walsh coached quarterbacks to throw to a spot, placed for the run after the catch and away from the nearest defender, and receivers to finish their routes so the spot would be right. Figure 59.4 shows three versions of one slant, and why only one of them is a good throw.

Show the Python
p = Play([O("X", "WR", -0.7, -14.0, "X"), O("QB", "QB", -5.0, 0.0, "Q")],
         [D("LCB", "CB", 9.5, -15.5, "CB"), D("WILL", "LB", 4.4, -3.0, "W")], los=35, to_go=None)
p.route("X", [(0, 0), (4.0, 0), (8.4, -5.6)], speed=6.8)
go_to(p, "LCB", (8.6, -12.2), kind="path", speed=4.0)                       # trailing, behind and outside
go_to(p, "WILL", (5.2, -4.6), kind="path", speed=3.0)                       # inside and underneath
p.highlight("X")
fld = draw(p, width_in=6.0, lateral=(-19.5, 0.5), window=(-2.5, 14.0), title=False)
ax = fld.ax
# X's position when the ball arrives, his direction of travel, and his run after the catch
catch = (6.6, -9.8)
px, py = (float(v) for v in _P(fld, p, *catch))
ax.add_patch(Circle((px, py), 0.8, fc="none", ec=style.OFFENSE, lw=1.2, ls=(0, (2, 1.4)), zorder=7))
xa, ya = (float(v) for v in _P(fld, p, 5.5, -11.2))
xb, yb = (float(v) for v in _P(fld, p, 7.3, -8.9))
ax.add_patch(FancyArrowPatch((xa, ya), (xb, yb), arrowstyle="-|>", mutation_scale=10, color=style.OFFENSE,
                             lw=1.4, zorder=8))
x0, y0 = (float(v) for v in _P(fld, p, 8.6, -7.0))
x1, y1 = (float(v) for v in _P(fld, p, 12.0, -3.2))
ax.add_patch(FancyArrowPatch((x0, y0), (x1, y1), arrowstyle="-|>", mutation_scale=11, color=style.OFFENSE_DARK,
                             lw=1.8, ls=(0, (3, 2)), zorder=6))


def ball(d, w, tag, col, dx=0.95, dy=0.35):
    bx, by = (float(v) for v in _P(fld, p, d, w))
    ax.add_patch(Ellipse((bx, by), 1.0, 0.62, fc=style.BALL, ec="white", lw=0.6, zorder=12))
    ax.text(bx + dx, by + dy, tag, fontsize=8.5, fontweight="bold", color=col, ha="center", va="center",
            zorder=13)


ball(8.0, -7.8, "A", GREEN, dx=-0.95)
ball(5.6, -11.0, "B", style.DEFENSE_DARK, dx=-0.95)
ball(6.2, -5.6, "C", style.DEFENSE_DARK, dx=0.95)
point_to(fld, p, (12.0, -12.6), (8.0, -7.8), "A: in front, on his line,\nbetween the defenders:\ncaught in stride",
         color=GREEN, fontsize=6.9, shrink=9)
point_to(fld, p, (1.6, -9.4), (5.6, -11.0), "B: behind him: he stops,\nthe corner closes",
         color=style.DEFENSE_DARK, fontsize=6.9, shrink=9)
point_to(fld, p, (1.8, -3.4), (6.2, -5.6), "C: too far inside:\ninto the Will", color=style.DEFENSE_DARK,
         fontsize=6.9, shrink=9)
say(fld, p, 12.4, -2.6, "run after\nthe catch", color=style.OFFENSE_DARK, fontsize=6.9)
plt.show()
Figure 59.4: Ball placement on a slant (illustrative close-up of the offense’s left side, seen from above). X (ringed) takes three hard steps, breaks inside and runs across the field; the dashed blue ring is where he is when the ball arrives, and the arrow through it is his direction of travel. The cornerback, who bailed at the snap, is trailing behind and outside him; the Will linebacker is inside and underneath. A: the right throw, a step in front of X on his line, in the open space between the two defenders. He catches it running, and the dashed arrow is his run after the catch. B: a throw behind him. He has to stop and reach back, and the trailing cornerback closes on him as he turns. C: a throw too far in front and inside. He has to lunge for it, and it leads him straight into the Will, who can hit him as it arrives or take the ball. A top-down picture can show only where the ball arrives, not how high: coaches want A at chest height.

The difference between A and B is perhaps a yard and a quarter of a second, and it is the difference between the play Walsh designed and a two-yard gain. That’s why the West Coast offense has a reputation for chewing up quarterbacks who aren’t accurate: the plays are easy to draw and hard to execute, and everything depends on a throw that has to be right every time. It’s also why its coaches have so often preferred an accurate quarterback with an average arm to the reverse. The cost of that preference shows up against defenses that crowd the short area and dare the quarterback to beat them deep.

NoteMadden vs. real life

In a video game, a pass is complete or it isn’t; the receiver catches it and runs. In real life, a “completion” covers everything from a catch in stride with open grass ahead to a lunge at a ball thrown behind a receiver who is tackled where he lands. Box scores count them the same, and coaches don’t. When you watch replays, look past caught or not to where on the receiver’s body the ball arrived, and whether he had to slow down to catch it.

Scripting and practice: taking the guesswork out

Walsh is remembered for one planning habit above all: before every game he scripted his first fifteen or so offensive plays (sometimes as many as 25) and called them in order.12 How a modern staff builds and uses an opening script, and whether scripted plays actually gain more, is the subject of The Game Plan; here the question is why it mattered so much to this offense in particular.

A timing offense can’t improvise its way out of trouble. A run-first team that has a bad week of practice can still line up and run its best play harder. A West Coast team whose quarterback and receivers aren’t in sync throws incompletions and interceptions. So Walsh treated the week as rehearsal for decisions, not just for plays. The script moved the play-calling decisions from the noise of the first quarter to the calm of the week, and it let the players rehearse the exact plays they’d run first, in order, until they ran them without thinking. It also turned the opening drives into a set of questions (how does this defense line up against split backs? what does it do when the flanker goes in motion?) whose answers set up the rest of the game.

The practices were built the same way. A 1990 review of Walsh’s book on building the 49ers describes a plan in which he counted out the days of training camp and the season in advance, used four practice formats (from a hard two-and-a-half-hour session to a light half-hour the day before a game), and never scheduled two hard sessions in a row. Individual technique was drilled to the step, down to how many steps the quarterback takes and when he looks for each receiver. Plays were repeated until they were automatic, and answers to special situations (“third down and four at the goal line”) were prepared in advance rather than invented during a game. His stated goal was that players “take the field to learn something each session,” not just to have their toughness tested. He also coached the coaches, which is the beginning of the tree at the end of this chapter.13

There’s a direct line from that practice field to the play this chapter opened with. The 49ers didn’t draw up Sprint Right Option in the dirt with fifty-eight seconds left. They had run it, including its second option, all season.

ImportantWhy it exists

Scripting and rehearsal exist because a precise offense is only as good as its worst repetition. Walsh’s plays left no slack in the timing, so he took the slack out of everything else: the order of the calls, the week’s schedule, the footwork. The trade-off is flexibility. A script is a plan made before the defense has shown its hand, and a coach who follows it too faithfully can call a play the defense is already set to stop, which is why every script has exits for third down, the red zone and bad field position.

Signature play: The Catch (1981 NFC Championship)

January 10, 1982, Candlestick Park. The winner would go to Super Bowl XVI. The Dallas Cowboys had been to five Super Bowls in the previous eleven seasons; the 49ers hadn’t been to one. Dallas cornerback Everson Walls had intercepted Montana twice, and his fumble recovery late in the game set up the touchdown that put Dallas ahead 27–21.

San Francisco got the ball at its own 11 with 4:54 left and all three timeouts. What happened next turned this chapter’s idea inside out. All day the 49ers had used the pass where a defense expected a run; now, with Dallas expecting passes, they used the run. The Cowboys played pass defense, and Walsh ran the ball: Lenvil Elliott, a backup running back, carried on several plays, and Freddie Solomon gained 14 yards on a reverse. In between came short throws to Solomon, Clark and the fullback Earl Cooper. A team famous for passing drove into field-goal range mostly by doing what the defense didn’t expect. By the time they reached the Dallas 6 on third-and-3, with fifty-eight seconds left, the 49ers had used two timeouts and had one left.

Why third-and-3, not third-and-goal? Because the line to gain was short of the goal line. A new set of downs gives the offense ten yards to gain, so a first down earned outside the 10 sets the line to gain in front of the goal line; on this series it was at the Dallas 3. The 49ers needed three yards for a first down, or six for the game. (Goal-to-go starts only when the line to gain would be at or beyond the goal line; see Downs, Distance, and Field Position.)

The call was Sprint Right Option; the full call in the 49ers’ playbook is “Change Left Slot – Sprint Right Option.”14 Read it the way Offensive Systems taught you to read a West Coast call: the first half is the formation, the second is the play. “Left Slot” names a formation with two receivers on one side, one of them in the slot; “Sprint Right” is the quarterback’s launch point, with the line and backs protecting for a quarterback running to his right; “Option” names the pass pattern. We couldn’t find “Change” defined in a source we could check. Solomon actually lined up in the right slot, so it may have flipped the formation, but treat that as a guess. The 49ers lined up in split backs with Solomon in the slot on the right and Clark wide outside him. The design:

  1. Montana sprints right. A sprint-out moves the whole passing game toward one sideline. The quarterback gets away from the middle of the rush and runs toward his receivers, and the throws get shorter and simpler, because he only has to read half the field.
  2. Solomon is the first option, on a short out toward the right corner of the end zone. The 49ers had scored on this exact play, to Solomon, earlier in the game.
  3. Clark is the second option. He releases inside, across the path of Solomon’s defender, a rub that makes it harder to stay with Solomon, then runs across the end zone to the left, stops, and comes back to the right along the end line, where the quarterback, still moving right, will find him.

Figure 59.5 recreates it, with Dallas in a version of the Flex, Tom Landry’s 4-3 with two linemen set back off the ball.15

Show the Python
LOS_C = 104.0          # the Dallas 6: BDB x = 110 - 6, so the goal line is d = 6 and the end line d = 16
off = line() + [O("QB", "QB", -1.9, 0.0, "Q"), O("HB", "RB", -5.0, -3.2, "H"), O("FB", "FB", -5.0, 3.2, "F"),
                O("Y", "TE", -0.7, -4.8, "Y"), O("SOL", "WR", -1.5, 8.2, "88"), O("CLK", "WR", -0.7, 13.2, "87")]
# a Flex-style front: the end on the tight end's side and the opposite tackle set back off the ball
dfn = [D("DEL", "DE", 1.7, T("5", -1) - 0.4, "E"), D("DTL", "DT", 1.0, T("2i", -1), "T"),
       D("DTR", "DT", 1.7, T("3"), "T"), D("JONES", "DE", 1.0, T("5") + 0.3, "72"),
       D("LBL", "LB", 3.6, -4.2, "L"), D("LBM", "LB", 3.8, 0.6, "L"), D("LBR", "LB", 3.6, 5.0, "L"),
       D("WALLS", "CB", 3.6, 14.0, "24"), D("SS", "SS", 2.6, 6.6, "SS"),
       D("LCB", "CB", 4.0, -10.0, "CB"), D("FS", "FS", 8.0, -1.0, "FS")]
CATCH_OFF, CATCH_DFN = off, dfn
catch = Play(off, dfn, los=LOS_C, ball_y="right", to_go=3)     # right hash: the right sideline is w = 23.6
SIDELINE_W = catch.ball_y                                       # BDB y = 0
# offense
go_to(catch, "QB", (-8.0, 19.4), kind="path", via=[(-4.6, 5.0), (-6.6, 11.0), (-7.6, 16.0)], speed=5.6)
for k, dw in zip(OL_KEYS, (0.6, 0.8, 1.0, 1.0, 1.2)):
    catch.block(k, pts=[(0.2, dw)])
catch.block("Y", pts=[(0.3, 0.4)])
go_to(catch, "FB", (-1.4, 7.0), kind="block", speed=4.0)
go_to(catch, "HB", (-3.6, 8.0), kind="block", via=[(-4.4, 3.0)], speed=4.0)
go_to(catch, "SOL", (6.8, 11.0), kind="route", via=[(5.6, 8.4)], speed=6.4)                 # the out... and the slip
go_to(catch, "CLK", (12.2, 4.4), kind="route", via=[(4.6, 12.6), (8.4, 8.0)], speed=7.0)     # inside, over Solomon's break
go_to(catch, "CLK", (14.8, 11.4), kind="route", via=[(14.3, 8.6)], delay=0.35, speed=5.4)   # back along the end line
# defense
go_to(catch, "WALLS", (9.8, 6.4), kind="path", via=[(6.8, 12.0)], delay=0.35, speed=4.6)   # trails Clark across
go_to(catch, "WALLS", (13.4, 9.6), kind="path", via=[(12.4, 7.4)], delay=0.3, speed=4.2)    # and back, underneath
go_to(catch, "SS", (5.2, 10.0), kind="path", via=[(4.4, 7.8)], delay=0.5, speed=4.4)        # rubbed, then jumps the out
go_to(catch, "JONES", (-6.2, 16.0), kind="rush", via=[(-2.4, 8.4)], delay=0.15, speed=5.0)
go_to(catch, "DTR", (-5.6, 13.8), kind="rush", via=[(-2.8, 6.0)], delay=0.2, speed=4.6)
go_to(catch, "LBM", (-4.4, 11.8), kind="rush", via=[(-0.8, 4.4)], delay=0.5, speed=4.6)
catch.rush("DEL", pts=[(-1.2, 0.6)], speed=2.2)
catch.rush("DTL", pts=[(-0.9, 0.4)], speed=2.0)
catch.drop("LBL", (7.6, -3.2))
catch.drop("LBR", (7.0, 4.8))
catch.drop("FS", (11.6, 1.0))
catch.drop("LCB", (9.0, -8.6))
T_THROW = 3.3
catch.pass_to("CLK", t=T_THROW)
T_CATCH = catch_time(tracking(catch), "CLK", T_THROW)

CATCH_MOMENTS = [
    (0.0, "", "Split backs: Solomon (88) in the slot, Clark\n(87) wide. Montana will sprint right:\nSolomon is option 1, Clark option 2."),
    (2.0, "after the rub", "Clark's trail crosses over Solomon's break:\nthe rub that Solomon's man (SS) had to work\nthrough. Solomon has slipped: option 1 is gone."),
    (T_THROW, "the throw", "Clark has crossed and come back along the\nend line. Montana, near the sideline, pump-\nfakes (Jones, 72, leaves his feet) and throws."),
    (round(T_CATCH, 1), "the catch", "High, where only Clark can reach it.\n49ers 27, Cowboys 27; the extra point\nwins it, 28–27."),
]
CATCH_LAT, CATCH_WIN = (-6.0, 25.0), (-9.5, 16.0)


def catch_after(fld, p, t, i):
    ax = fld.ax
    # the end line and the sideline, drawn heavy because the play is about both
    (xa, ya), (xb, yb) = (fld.xy(120.0, y) for y in (0.0, FIELD_W))
    ax.plot([float(xa), float(xb)], [float(ya), float(yb)], color=style.INK, lw=2.0, zorder=2)
    (xa, ya), (xb, yb) = (fld.xy(x, 0.0) for x in (p.los + CATCH_WIN[0], 120.0))
    ax.plot([float(xa), float(xb)], [float(ya), float(yb)], color=style.INK, lw=2.0, zorder=2)
    if i <= 0:
        say(fld, p, 11.0, -2.0, "end zone", color=style.INK_2, fontsize=6.6)
        say(fld, p, 14.6, -2.0, "end line", color=style.INK, fontsize=6.6)
        say(fld, p, -6.4, 21.2, "side-\nline", color=style.INK, fontsize=6.4)
    if i == 1 or (i == -1 and 1.1 <= t <= 2.4):
        say(fld, p, 5.0, 13.6, "slip", color=style.DEFENSE_DARK, fontsize=6.8, weight="bold")


FIELD_W = 160 / 3
animation_or_strip(catch, CATCH_MOMENTS, lateral=CATCH_LAT, window=CATCH_WIN, ncols=2, width_in=6.2,
                   rings=(("QB", GREEN), ("CLK", GREEN)), after=catch_after, mark_r=0.72, note_h=0.7,
                   trails=("QB", "CLK", "SOL", "WALLS", "SS", "JONES"))
Figure 59.5: The Catch, recreated (simulated, BDB-format tracking). The 49ers’ backs and receivers follow contemporary accounts; the tight end’s side, the Cowboys’ alignment (a Flex-style 4-3, with two linemen set back off the ball) and the exact paths are illustrative, not traced from film. Third-and-3 at the Dallas 6, 58 seconds left: the orange line is the line to gain at the 3, the grey area is the end zone, the thick line at the top is the end line, and the right edge of the turf is the sideline. Freddie Solomon (88) is in the right slot and Dwight Clark (87) wide right. Montana (Q, ringed) sprints right. Clark (ringed) releases inside, across the path of Solomon’s defender (the rub), while Solomon, the first option, breaks out at the goal line and slips. Clark runs across the end zone to the left, stops, and comes back to the right along the end line as Ed ‘Too Tall’ Jones (72) and two other Cowboys chase Montana to within a few yards of the sideline. Montana pump-fakes, then throws high to the back of the end zone, where only Clark can reach it, over Everson Walls (24). Faint lines are the paths of the quarterback, the two receivers and their defenders, and Jones.

Look at the third panel. That’s the moment that gets called luck, and it’s the moment the design is working. The first option is gone: Solomon has slipped, and the Cowboys have him covered anyway. Montana is out of room, a few yards from the sideline with three defenders closing. A quarterback without a plan has two bad choices. He can take the sack, which loses yards and a down. Or he can throw the ball away, which is safe but gives up the down for nothing: fourth down, one play left to score. Montana had a third choice because the play had one. He knew Clark would be coming back along the end line, so he pump-faked to buy half a second, got Jones off his feet, and threw the ball where only a 6-foot-4 receiver jumping at the back of the end zone could catch it. That throw was as safe as a throwaway, because if Clark couldn’t reach it, it would land out of bounds: no interception, and fourth down still to play. But unlike a throwaway, it could also score.

Was it a throwaway that happened to be caught? People have argued about it ever since, and Montana’s own description of the throw has varied over the years. Clark’s answer never did: the route back along the end line was a practised backup, and he knew where the ball would come. Both things can be true. The throw was a quarterback under pressure putting the ball where nothing bad could happen; the route that put Clark under it was in the playbook and on the practice schedule. That is the practice-field argument of the last section in one snap.

The rest is history. Ray Wersching’s extra point made it 28–27. Dallas still had 51 seconds and moved to midfield, but Lawrence Pillers sacked Danny White, White fumbled, and the 49ers recovered. Two weeks later they beat Cincinnati, the team Walsh had left, 26–21 in Super Bowl XVI.

ImportantWhy the sprint-out exists

The sprint-out (the quarterback running toward one sideline at the snap, with the routes flowing the same way) trades half the field for simplicity and time. The quarterback reads two or three receivers on one side instead of five across the field; he throws on the run, closer to his receivers; and he moves away from the rushers who started in the middle. Near the goal line, where the field is short and the end zone has edges a ball can be thrown over, those trades are worth more. The costs are real too: the whole back side of the field is gone, the throws are harder to make on the move, and a defense that knows the sprint is coming can chase it, as Dallas did. The modern cousins of the sprint-out are the bootleg and the moving-pocket throws you’ll meet in Play-Action, Boots, and Screens.

The dynasty, and the system that outlived its coach

The Catch started a run that lasted longer than Walsh did. The table lists the 49ers’ Super Bowl seasons of the next fourteen years.16

Season Head coach Regular season Super Bowl
1981 Bill Walsh 13–3 XVI: beat Cincinnati 26–21
1984 Bill Walsh 15–1 XIX: beat Miami 38–16
1988 Bill Walsh 10–6 XXIII: beat Cincinnati 20–16
1989 George Seifert 14–2 XXIV: beat Denver 55–10
1994 George Seifert 13–3 XXIX: beat San Diego 49–26

Walsh retired after Super Bowl XXIII with a regular-season record of 92–59–1 and a 10–4 record in the playoffs. His defensive coordinator, George Seifert, took over and won two more titles with the same offense, the second with Steve Young at quarterback and Mike Shanahan as offensive coordinator. A scheme that keeps winning after its inventor leaves is a scheme that was taught, not just called.

Walsh’s last Super Bowl was already a family meeting. Cincinnati’s head coach, Sam Wyche, had run the 49ers’ passing game as Walsh’s assistant from 1979 to 1982, took the system to the Bengals in 1984, and made the no-huddle their everyday offense rather than a two-minute tool. Montana beat him with a touchdown pass with 34 seconds left.17

NoteFilm room: 49ers 1984 season, Super Bowl XIX

January 20, 1985, Stanford Stadium. The 15–1 49ers against Dan Marino’s Dolphins, who had just set passing records. San Francisco won 38–16, Montana was the game’s MVP, and Roger Craig scored three touchdowns, two of them on catches, the first player to score three in a Super Bowl.18

What to notice: the running back as a receiver. Watch Craig’s routes out of the backfield: he isn’t the last resort when everyone else is covered, he’s a planned target, often lined up against a linebacker who can’t run with him. Watch for him stemming straight at that linebacker and breaking away from his leverage, in or out: the option route. A defense built to stop a two-back running team has the wrong players on the field to cover two backs who run routes.

Where to watch: NFL Films made an episode of its America’s Game series on each of the four Walsh-era and Seifert-era title teams (1981, 1984, 1988 and 1989), first shown on NFL Network in 2006 and 2007, and it’s the easiest place to see this offense on film. Highlights of The Catch are on the NFL’s official channels.19

NoteFilm room: 49ers 1989 season, Super Bowl XXIV

January 28, 1990, New Orleans: 49ers 55, Broncos 10, still the most lopsided Super Bowl ever played. Montana threw five touchdown passes, then a Super Bowl record, three of them to Jerry Rice.20 It was Seifert’s first season as head coach.

What to notice: the same offense without its inventor. The routes, the timing and the reads are Walsh’s; the play-caller isn’t. Watch how often the ball is out of Montana’s hand before Denver’s rush arrives, and how often Rice and Taylor catch it in stride and keep going.

The data fingerprint

The 1980s 49ers predate the free play-by-play data this book uses (nflverse, which starts in 1999 and which Part 13 teaches you to load), so their fingerprint has to come from the season totals: an offense that completed passes at rates other teams couldn’t match, threw relatively few interceptions for how often it threw, and had a running back lead the league in catches. Montana led the NFL in completion percentage five times (1980, 1981, 1985, 1987 and 1989), and his 1989 passer rating (the league’s traditional all-in-one passing grade, built from completion rate, yards, touchdowns and interceptions per attempt), 112.4, was the highest any quarterback had posted in a season to that point.21

What can be measured is whether Walsh’s central claim holds up today: that a short pass can do a run’s job, and do it better. Figure 63.6 splits every early-down play in competitive games from 2023 to 2025 (we drop plays where the win probability, the chance from the score and clock that the offense goes on to win, was below 10% or above 90%, so blowouts and kneel-downs don’t distort it; Win Probability builds the idea) by what the offense did, a designed run or a pass of a given depth, and measures each by success rate (the share of plays that left the offense better off) and by EPA per play.

Show the Python
style.apply()
fig, (a1, a2) = plt.subplots(1, 2, figsize=(6.6, 3.6), sharey=True)
labels = list(TAB.index)
y = np.arange(len(labels))[::-1]
cols = [style.MUTED, style.SEQUENTIAL_BLUE[2], style.SERIES[0], style.SEQUENTIAL_BLUE[2],
        style.SEQUENTIAL_BLUE[2], style.SEQUENTIAL_BLUE[2], style.INK_2]
for ax, col, fmt, lab in ((a1, "sr", "{:.0%}", "Success rate"), (a2, "epa", "{:+.2f}", "EPA per play")):
    vals = TAB[col].values
    ax.barh(y, vals, color=cols, height=0.62, zorder=3)
    ax.axvline(0, color=style.BASELINE, lw=0.9, zorder=2)
    for yy, v in zip(y, vals):
        ax.text(v + (0.008 if col == "sr" else (0.012 if v >= 0 else -0.012)), yy, fmt.format(v),
                va="center", ha="left" if (v >= 0) else "right", fontsize=7.2, color=style.INK)
    ax.set_title(lab, fontsize=9)
    ax.grid(axis="y", visible=False)
    ax.tick_params(axis="x", labelsize=7)
a1.set_xlim(0, 0.75)
a1.xaxis.set_major_formatter(plt.FuncFormatter(lambda v, _: f"{v:.0%}"))
a2.set_xlim(-0.27, 0.62)
a2.tick_params(axis="y", length=0)
a1.set_yticks(y)
a1.set_yticklabels([f"{lab}\n({int(n):,} plays)" for lab, n in zip(labels, TAB.n)], fontsize=7, color=style.INK_2)
a1.tick_params(axis="y", length=0)
plt.tight_layout(w_pad=1.0)
plt.show()
Figure 59.6: Walsh’s claim, tested on modern data. Every first- and second-down snap with the win probability between 10% and 90% (no blowouts), 2023-2025 regular seasons. Left: success rate. Right: expected points added per play. Passes are grouped by how far the ball was thrown past the line (air yards), with incompletions and interceptions counted. A designed run (scrambles and kneels excluded) and a pass thrown behind the line (screens, swings) do almost exactly the same job. A pass thrown one to five yards past the line, the heart of the horizontal passing game, succeeds far more often than either, and the six-to-fifteen-yard throws of Walsh’s five-step game more often still. The last bar puts the sacks and scrambles back in, which is the fair comparison for ‘passing’ as a whole. Data: nflverse play-by-play via nflreadpy; course calculation.

Read the top three bars together. A designed run on an early down succeeded 39% of the time and gained 4.4 yards on average. A pass thrown behind the line, a screen or a swing, succeeded 39% of the time: the same job, done about as well. A pass that travelled one to five yards in the air succeeded 53% of the time, with incompletions counted as failures. That’s Walsh’s argument in today’s numbers: the short throw is a better run, more consistent and worth more expected points, and about 54% of the yards on those completions came after the catch.

Now read down. The best bar on the left belongs to throws of six to ten yards, which succeeded 63% of the time, and the 11-to-15-yard throws aren’t far behind. That band is where Walsh’s five-step game lived: the digs, crossers and curls you saw in Figure 59.3, thrown on time to a receiver on the move. The deep throws are worth the most per play but succeed less than half the time and carry most of the interceptions. And the last bar is the honest reminder that “passing” includes sacks and scrambles, which bring the average for every dropback back down: a short throw isn’t better than passing, it’s better than running, which is the claim Walsh made.

That raises the analyst’s obvious question. If the deep bars are worth the most per play, was Walsh’s short-first offense wrong? Three things to weigh. First, the bars are averages over plays that weren’t assigned at random: a quarterback throws deep when the coverage allows it, often because the short throws have pulled defenders forward, so the deep bar takes credit for a look the short game helped create. This chart shows association, not a controlled test. Second, the deep throw’s average hides its spread: it fails more than half the time, and an offense that lives on it lives with incompletions, sacks while waiting for routes to develop, and interceptions. Third, Walsh’s real competitor was the run, and modern data agrees with him there emphatically. Where today’s analytics would push him is on the mix. Even with sacks and scrambles counted, an early-down dropback averaged +0.08 EPA against -0.07 for a designed run, so a modern Walsh would probably throw more often on first down, and more of those throws in the six-to-fifteen-yard band his five-step game was built for. Whether his short game was optimal or just optimal for a team with a weak line in 1979 is a fair argument; it was certainly better than what it replaced.

The league has absorbed the lesson so completely that it no longer looks like a system. In these three seasons 55% of early-down throws travelled five yards or less in the air, and 26% were thrown to a receiver at or behind the line. Every team in the league now runs some of Walsh’s offense, which is the subject of the coaching tree two sections down.

CautionCommon misconception

“Short passes are just safe passes.” The chart says something stronger and something weaker. Stronger: on early downs, short passes beat runs on consistency, which is what an offense needs to stay ahead of the chains. Weaker: a short pass is not automatically a good play. Its value comes from the defense leaving room underneath, and a defense that sits on short throws can make them fail. And on third-and-8 a five-yard catch is a punt, however efficient it looks on first down; there the deeper throw is the right one (Third Down).

How opponents responded

A system that wins three Super Bowls gets studied, and defenses found four answers. Each attacked one of the design goals.

Hit the receivers, and the quarterback. The timing depends on receivers reaching their spots on time, and inside five yards contact is still legal, so the first answer was to jam receivers at the line and knock them off schedule. The physical extreme was Bill Parcells’s New York Giants, who beat the 49ers in the playoffs after the 1985 season (17–3) and the 1986 season (49–3), the second game remembered for the hit by nose tackle Jim Burt that knocked Montana out of it.22 One story credits Parcells with the name “West Coast offense” itself, as a jab after the first of those wins; another traces it to a 1993 remark by quarterback Bernie Kosar, publicized in Sports Illustrated, in which the label meant the Coryell family of offenses from San Diego and was then attached to Walsh’s by mistake. Walsh disliked it either way.23

Rush with safe pressure. A quick-throw offense beats an ordinary blitz by getting the ball out before it arrives, so defenses invented pressure that didn’t leave a hole behind it. The Cincinnati Bengals, Walsh’s Super Bowl opponent twice, made Dick LeBeau their defensive coordinator in 1984 and became the home of the zone blitz, which drops a lineman into the throwing lane of a hot route while a linebacker rushes, and which the NFL’s own history describes as built to stop the West Coast timing game; the story is in Zone Blitzes.24

Keep everything in front, and tackle. If the offense lives on yards after the catch, take away the “after.” The Tampa 2 of the late 1990s and 2000s was built on fast linebackers and corners who sat short, let the short throw be completed, and swarmed the catcher. Its best-known coach, Tony Dungy, had been Dennis Green’s defensive coordinator in Minnesota, and Green was a Walsh assistant: the tree was learning to stop itself (Cover 2 and Tampa 2).

Learn the language. The West Coast call is long and explicit, and once a few dozen coaches had learned it, they all spoke it. The best-known payoff came in Super Bowl XXXVII, after the 2002 season, when Jon Gruden’s Tampa Bay Buccaneers beat the Oakland Raiders, the team he had coached the year before, 48–21. Rich Gannon threw five interceptions, and Raiders players said afterwards that Oakland had kept much of Gruden’s terminology and that Tampa’s defenders seemed to know what was coming. (Derrick Brooks remembers it differently: Gruden played Gannon himself in practice, and “we didn’t know what play they was running, but we knew their personalities.”)25

Today’s version of the answer is the two-high safety shell of the 2020s, which takes away the deep throw first and rallies to everything short. The West Coast descendants answered that in turn, with under-center play-action and the outside-zone run game, which is where the Shanahan case study begins.

The coaching tree

A coaching tree is the family of coaches descended from one head coach, traced through who worked on whose staff. A branch runs from a head coach or coordinator to an assistant who later ran a staff of his own; because most coaches work for several bosses, one coach can sit on several branches. Walsh’s is usually called the largest in football. Figure 69.7 draws the branches that run through his offensive assistants, and the two boxes underneath list the head coaches who came out of it in the 2025 and 2026 seasons.

Why did one staff produce so many head coaches? Partly luck and winning, which gets assistants hired. But Walsh also built a system that could be carried away. Everything was written down: the 1982 playbook ran past 300 pages (the call for The Catch is on page 325), and he later co-wrote a coaching manual, Finding the Winning Edge, on how to run a staff, a practice week and a passing game.26 The plays came with their reasons attached, as this chapter has shown: why this drop goes with that route, why the back checks before he releases. And everything was scripted and rehearsed, so his assistants spent their weeks planning and teaching the system, not just carrying it out; in the words of the 1990 review quoted earlier, he “coached the coaches.” An assistant who leaves with a written, explained, rehearsed system can install it somewhere else. One who leaves with a feel for his boss’s play calls can’t.

Show the Python
# (key, name, line under the name, column, row); children sit level with or just below their parent
NODES = [
    ("walsh", "Bill Walsh", "49ers 1979–88", 0, 3.4),
    ("seifert", "George Seifert", "49ers 1989–96,\nPanthers 1999–2001", 1, 0.6),
    ("holmgren", "Mike Holmgren", "Packers 1992–98,\nSeahawks 1999–2008", 1, 3.0),
    ("green", "Dennis Green", "Vikings 1992–2001,\nCardinals 2004–06", 1, 5.0),
    ("hackett", "Paul Hackett", "49ers asst. 1983–85", 1, 6.3),
    ("wyche", "Sam Wyche", "Bengals 1984–91,\nBuccaneers 1992–95", 1, 7.5),
    ("mshan", "Mike Shanahan", "Raiders 1988–89;\nBroncos 1995–2008;\nWashington 2010–13", 2, 0.6),
    ("gruden", "Jon Gruden", "Raiders 1998–2001,\n2018–21; Bucs 2002–08", 2, 2.0),
    ("reid", "Andy Reid", "Eagles 1999–2012,\nChiefs 2013–", 2, 3.6),
    ("mariucci", "Steve Mariucci", "49ers 1997–2002,\nLions 2003–05", 2, 4.85),
    ("billick", "Brian Billick", "Ravens 1999–2007", 2, 6.0),
    ("mccarthy", "Mike McCarthy", "Packers 2006–18, Cowboys\n2020–24, Steelers 2026–", 2, 7.2),
    ("kyle", "Kyle Shanahan", "49ers 2017–", 3, 0.0),
    ("gkub", "Gary Kubiak", "Texans 2006–13,\nBroncos 2015–16", 3, 1.05),
    ("mcvay", "Sean McVay", "Rams 2017–", 3, 2.1),
    ("harbaugh", "John Harbaugh†", "Ravens 2008–25;\nGiants 2026–", 3, 3.3),
    ("pederson", "Doug Pederson", "Eagles 2016–20,\nJaguars 2022–24", 3, 4.35),
    ("childress", "Brad Childress", "Vikings 2006–10", 3, 5.4),
    ("nagy", "Matt Nagy", "Bears 2018–21", 3, 6.45),
    ("mlaf", "Matt LaFleur", "Packers 2019–", 4, 0.0),
    ("mcdaniel", "Mike McDaniel", "Dolphins 2022–25", 4, 0.95),
    ("milaf", "Mike LaFleur", "Cardinals 2026–", 4, 1.9),
    ("kkub", "Klint Kubiak", "Raiders 2026–", 4, 2.85),
    ("oconnell", "Kevin O'Connell", "Vikings 2022–", 4, 3.8),
    ("taylor", "Zac Taylor", "Bengals 2019–", 4, 4.75),
    ("coen", "Liam Coen", "Jaguars 2025–", 4, 5.7),
    ("stefanski", "Kevin Stefanski", "Browns 2020–25;\nFalcons 2026–", 4, 6.65),
]
EDGES = [("walsh", k) for k in ("seifert", "holmgren", "green", "hackett", "wyche")] + [
    ("seifert", "mshan"), ("holmgren", "gruden"), ("holmgren", "reid"), ("holmgren", "mariucci"),
    ("green", "billick"), ("hackett", "mccarthy"),
    ("mshan", "kyle"), ("mshan", "gkub"), ("mshan", "mcvay"), ("reid", "harbaugh"), ("reid", "pederson"),
    ("reid", "childress"), ("reid", "nagy"),
    ("kyle", "mlaf"), ("kyle", "mcdaniel"), ("kyle", "milaf"), ("gkub", "kkub"), ("mcvay", "oconnell"),
    ("mcvay", "taylor"), ("mcvay", "coen"), ("childress", "stefanski")]
SECOND = [("gruden", "kyle"), ("gruden", "mcvay")]
HC25 = ["Andy Reid (KC)", "Kyle Shanahan (SF)", "Sean McVay (LAR)", "Matt LaFleur (GB)", "Kevin O'Connell (MIN)",
        "Zac Taylor (CIN)", "Liam Coen (JAX)", "John Harbaugh (BAL)", "Mike McDaniel (MIA)", "Kevin Stefanski (CLE)"]
HC26 = ["Andy Reid (KC)", "Kyle Shanahan (SF)", "Sean McVay (LAR)", "Matt LaFleur (GB)", "Kevin O'Connell (MIN)",
        "Zac Taylor (CIN)", "Liam Coen (JAX)", "John Harbaugh (NYG)", "Kevin Stefanski (ATL)",
        "Mike McCarthy (PIT)", "Klint Kubiak (LV)", "Mike LaFleur (ARI)"]

W, H = 6.6, 7.1
fig = plt.figure(figsize=(W, H))
ax = fig.add_axes([0, 0, 1, 1])
ax.set_xlim(0, W); ax.set_ylim(0, H); ax.axis("off")
COLX = [0.04, 1.26, 2.6, 3.92, 5.4]          # the widest gap is the last, where four parents' lines fan out
BOX_W, BOX_H = 1.1, 0.42
TOP = H - 0.42
ROW = 0.55
GEN = ["Head coach", "His assistants", "Next generation", "Third generation", "Fourth generation"]
for x, g in zip(COLX, GEN):
    ax.text(x + BOX_W / 2, H - 0.16, g, ha="center", va="center", fontsize=7.4, color=style.INK_2, fontweight="bold")
pos = {}
for key, name, sub, c, rr in NODES:
    x, y = COLX[c], TOP - rr * ROW - BOX_H
    pos[key] = (x, y)
    root = key == "walsh"
    ax.add_patch(FancyBboxPatch((x, y), BOX_W, BOX_H, boxstyle="round,pad=0.02,rounding_size=0.05",
                                fc=style.OFFENSE_TINT if root else style.SURFACE,
                                ec=style.OFFENSE_DARK if root else style.BASELINE, lw=1.2 if root else 0.8,
                                zorder=3))
    ax.text(x + 0.05, y + BOX_H - 0.1, name, fontsize=6.9, fontweight="bold", color=style.INK, va="center",
            zorder=4)
    ax.text(x + 0.05, y + (BOX_H - 0.17) / 2, sub, fontsize=5.4, color=style.INK_2, va="center", zorder=4,
            linespacing=1.0)


# colour each line by the first-generation branch it descends from
BRANCH = {"seifert": style.SERIES[0], "holmgren": style.SERIES[1], "green": style.SERIES[2],
          "hackett": style.SERIES[3], "wyche": style.MUTED}
PARENT = {b: a for a, b in EDGES}


def branch_col(k):
    while k in PARENT and PARENT[k] != "walsh":
        k = PARENT[k]
    return BRANCH.get(k, style.MUTED)


# each parent gets its own vertical trunk inside the gap; the highest parent's trunk is the rightmost, so a
# parent's lines never cross the trunk of a parent below it
COLN = {k: c for k, _, _, c, _ in NODES}
TRUNK = {}
for c in range(4):
    parents = sorted({a for a, _ in EDGES + SECOND if COLN[a] == c}, key=lambda k: -pos[k][1])
    gap0, gap1 = COLX[c] + BOX_W, COLX[c + 1]
    for j, k in enumerate(parents):
        TRUNK[k] = gap0 + (gap1 - gap0) * (0.5 if len(parents) == 1 else 0.8 - 0.6 * j / (len(parents) - 1))


def link(a, b, ls="-", col=None, lw=1.0, dy=0.0):
    (xa, ya), (xb, yb) = pos[a], pos[b]
    x0, y0 = xa + BOX_W, ya + BOX_H / 2 + dy
    x1, y1 = xb, yb + BOX_H / 2 + dy
    xm = TRUNK[a]
    ax.plot([x0, xm, xm, x1], [y0, y0, y1, y1], color=col or branch_col(b), lw=lw, ls=ls, zorder=1,
            solid_joinstyle="round", dash_capstyle="butt")


for a, b in EDGES:
    link(a, b)
for a, b in SECOND:
    link(a, b, ls=(0, (2.2, 1.8)), col=style.MUTED, lw=0.9, dy=-0.11)

# the 2025 and 2026 boxes (neutral outlines: their colours mean nothing about branches)
by = 0.08
bh = 1.66
for i, (lab, names) in enumerate((("Head coaches from this tree, 2025 season", HC25),
                                  ("Head coaches from this tree, 2026 season", HC26))):
    bx = 0.08 + i * 3.28
    ax.add_patch(FancyBboxPatch((bx, by), 3.12, bh, boxstyle="round,pad=0.02,rounding_size=0.06",
                                fc=style.SURFACE, ec=style.INK_2, lw=1.0, zorder=3))
    ax.text(bx + 0.1, by + bh - 0.17, f"{lab}: {len(names)} of 32", fontsize=7.3, fontweight="bold",
            color=style.INK, va="center")
    half = int(np.ceil(len(names) / 2))
    for j, n in enumerate(names):
        cx = bx + 0.12 + (j // half) * 1.52
        cy = by + bh - 0.43 - (j % half) * 0.215
        ax.text(cx, cy, "• " + n, fontsize=6.6, color=style.INK, va="center")
plt.show()
Figure 59.7: The Walsh coaching tree: the branches through his offensive assistants (selected). A solid line means the coach on the right worked on the staff of the coach on the left, coloured by the first-generation branch it comes from: blue for Seifert (and through him Mike Shanahan), orange for Holmgren, green for Dennis Green, amber for Hackett, grey for Wyche. A grey dashed line marks an important second mentor; most coaches had several, and only a few are drawn. Years are NFL head-coaching tenures unless marked. Mike Shanahan is the link to the modern wide-zone family: he never worked for Walsh, but was George Seifert’s offensive coordinator from 1992 to 1994. Sean McVay’s first NFL job was on Jon Gruden’s staff (dashed), but he learned his trade in Washington from 2010 to 2013 under Mike Shanahan (solid). † John Harbaugh coached special teams on Andy Reid’s staff, not offense; he’s here because he became a head coach out of it. The two boxes list the tree’s NFL head coaches in the 2025 season and in the 2026 season, after a winter of moves (checked October 2026). Defensive coaches who came through these staffs (Mike Macdonald, Robert Saleh, DeMeco Ryans and others) are left off.

Read it from left to right and three big branches stand out, with two smaller ones below them.

Holmgren’s branch is the one that kept the name. Mike Holmgren coached Walsh’s quarterbacks from 1986 to 1988 and was Seifert’s offensive coordinator, then won Super Bowl XXXI with Brett Favre’s Packers (after the 1996 season). His Green Bay staffs produced Andy Reid, Jon Gruden and Steve Mariucci, and Reid’s staffs in Philadelphia produced so many head coaches (John Harbaugh, Doug Pederson, Brad Childress, Matt Nagy, and others) that his branch is a tree of its own.27 It is also the branch that changed the most. Reid’s Chiefs, covered in their own case study, still speak Walsh’s language, but their offense has absorbed college spread concepts, run-pass options and a great deal of improvisation.

The Hackett branch is the quiet one. Paul Hackett coached for Walsh from 1983 to 1985 and later taught the system to a young quarterbacks coach, Mike McCarthy, at the University of Pittsburgh and then in Kansas City; McCarthy won Super Bowl XLV with the Packers, coached the Cowboys from 2020 to 2024, and in 2026 is the head coach in Pittsburgh.28 Two more short branches sit at the bottom of the figure. Dennis Green’s Vikings staff produced Brian Billick, his offensive coordinator from 1994 to 1998, who won Super Bowl XXXV with the Ravens.29 And Sam Wyche, as you saw, took the offense to Cincinnati.

The Shanahan branch is the one that rules the league today, and it has the most interesting connection: Mike Shanahan never worked for Walsh. He was Seifert’s offensive coordinator from 1992 to 1994, the years Steve Young ran Walsh’s offense, then took it to Denver, married it to the outside-zone running game and bootlegs, and won two Super Bowls. One of them, Super Bowl XXXII after the 1997 season, came against Holmgren’s Packers, 31–24: two branches of one tree meeting for the title, nine years after Walsh’s and Wyche’s teams did.30 Through his son Kyle, Gary Kubiak and Sean McVay, that branch produced most of the play-callers of the 2020s. McVay’s first NFL job was on Gruden’s staff in Tampa in 2008, which is why the figure gives him a dashed line from Gruden, but he learned his trade in Washington from 2010 to 2013, as a tight ends coach under Mike Shanahan with Kyle as offensive coordinator, which is why his solid line comes from Shanahan.31 That family is the subject of the Shanahan case study.

The 2025 and 2026 seasons, labelled separately. In the 2025 season, ten of the league’s 32 head coaches came from these branches. The winter changed the list more than it shrank it. Mike McDaniel was fired in Miami and became the Chargers’ offensive coordinator; John Harbaugh, fired in Baltimore after 18 seasons, became the Giants’ head coach; Kevin Stefanski moved from Cleveland to Atlanta. Three more coaches from the tree became head coaches: Klint Kubiak, the 2025 Seattle offensive coordinator, in Las Vegas; Mike LaFleur, the 2025 Rams offensive coordinator, in Arizona; and Mike McCarthy in Pittsburgh. In 2026 the tree has twelve head coaches. Super Bowl LX, after the 2025 season, was won by a Seattle staff with both a defensive and an offensive root in the tree: head coach Mike Macdonald came up on Harbaugh’s Baltimore staff, and offensive coordinator Klint Kubiak from the Shanahan family.32

CautionCommon misconception

“Same tree, same offense.” A coaching tree records where coaches learned, not what they run. Reid’s 2025 Chiefs, Shanahan’s 49ers and Harbaugh’s Ravens share a common ancestor and almost nothing on the field: shotgun spread and RPOs in Kansas City, under-center wide zone in San Francisco, and a quarterback-run offense in Baltimore (and Harbaugh coached special teams on Reid’s staff, not offense).33 The data agrees. In 2025 the ten tree teams threw 56% of their early-down passes five yards or less in the air, the other 22 teams 55%, which is no real difference, and Liam Coen’s Jaguars, a McVay-branch team, ranked 30th of 32 in short early-down throws. What travels down a tree is a way of working (the explicit language, the scripting, the attention to detail in practice) more than a playbook. Trees are also generous: almost every coach worked for several bosses, and the drawer chooses the line.

There’s one more branch the figure can’t draw. In the mid-1980s (1987, by the league’s account) Walsh began bringing minority coaches into the 49ers’ training camp, to give them NFL experience and a reference from an NFL staff at a time when the profession was almost entirely white. The league later adopted the program for every team; it now carries his name as the Bill Walsh NFL Diversity Coaching Fellowship. Dennis Green and Ray Rhodes, two of the first Black head coaches of the modern era, got their NFL starts on Walsh’s staffs. Herm Edwards was the first graduate of the program to become an NFL head coach, and Marvin Lewis, Lovie Smith, Mike Tomlin and Raheem Morris followed.34 That might be the most important tree he planted.

What survives

Forty-odd years later, almost nothing about the 1981 49ers looks modern: the split backs, the quarterback under center on nearly every snap, the seven-step drop. And almost everything they did is still here.

  • The short pass as a run. Every modern offense has a set of throws that does the running game’s job: the quick game, the screen, and the RPO, which goes one step further and lets the quarterback choose between the handoff and the throw after the snap (RPOs). The fingerprint chart above is the league running Walsh’s offense.
  • Backs as receivers. Roger Craig’s 1,000/1,000 season of 1985 was matched in 1999 by Marshall Faulk and in 2019 by Christian McCaffrey, then with Carolina, who was the 2025 Comeback Player of the Year in Kyle Shanahan’s descendant of the same offense in San Francisco.35
  • Timing and placement. Quarterbacks are still taught to throw on rhythm, with their feet as the clock, and to put the ball where the receiver can keep running. The Quarterback Play chapter is built on those ideas.
  • The script and the rehearsal. Nearly every NFL play-caller scripts the opening plays, and every staff plans its practice week to the minute. Andy Reid’s index card of first-and-second-down calls, in The Game Plan, is Walsh’s script, one branch down.
  • The language. The long, explicit West Coast call lives on in the Shanahan, McVay and Reid families. Jon Gruden’s favourite, “Spider 2 Y Banana,” reads like Sprint Right Option: a formation-and-protection piece (“Spider 2”), then one receiver and his route (the tight end, Y, runs the route the playbook calls “banana”), with the rest, including the fullback in the flat, left to the base pattern. Many staffs now shorten calls with wristbands and one-word names (Offensive Systems).

What faded is mostly the look. The two-back pro set gave way to one back and three receivers, and then to the shotgun; the fullback nearly disappeared (Shanahan’s 49ers are an exception). But the look is coming partly back: in 2025, offenses lined up with fewer than three wide receivers on 41.7% of plays, the most since the NFL’s tracking data began in 2016, and the quarterback was under center on about a third of snaps, up from about 27% in 2023.36 The descendants of Walsh’s tree are leading that return, for reasons the Shanahan case study explains.

Watch for it on Sunday

TipWatch for it: a West Coast census

Pick a team from the tree (the 49ers, Rams, Packers, Chiefs, Vikings, Bengals, Jaguars or any of the others in the 2026 box) and chart its first-half first and second downs. For every pass, note two things:

  • Where was the ball caught, or where would it have been? Behind the line, one to five yards past it, or deeper? Count the first two together as short horizontal throws.
  • Was the receiver running when he caught it? If yes, how far did he go after the catch?

Then do the arithmetic. Across the league in 2025, 56% of early-down throws in competitive games travelled five yards or less in the air; teams ranged from 46% (CHI) to 64% (PIT). A team well above the league figure is leaning on the horizontal game; a team well below it is throwing downfield on early downs and running to set up its short game.

Don’t expect a tree team to land high just because it’s a tree team. In 2025 the tree’s ten teams averaged almost exactly what everyone else did (see the coaching-tree section): the short game is now the whole league’s, and what your count tells you is how this staff uses it this week.

Post-snap, on the replay: pick one short completion and watch the receiver’s hands. Did the ball arrive in front of him, at chest height, so he never slowed down (A in Figure 59.4), or behind him? And look for the backs: count how many times a running back releases into a route after checking for a blitzer. That’s the 1984 49ers, forty years on.

Predict the play

TipPredict the play 1: eight in the box
Show the Python
dfn = era_43("one")
dfn = [q.moved(d=4.2, w=8.8) if q.key == "SS" else q for q in dfn]
dfn = [q.moved(d=8.0) if q.key in ("LCB", "RCB") else q for q in dfn]
p = Play(split_backs(), dfn, los=50, to_go=6)
p.highlight("SS")
fld = draw(p, width_in=5.0, lateral=(-18.5, 18.0), window=(-7.0, 15.5), title=False)
ruler(fld, p, depths=(5, 10, 15))
say(fld, p, 8.0, 5.4, "SS walked down:\n8 in the box", color=style.DEFENSE_DARK, fontsize=7)
say(fld, p, 10.6, 13.5, "corners 8 yards off", color=style.DEFENSE_DARK, fontsize=6.8)
say(fld, p, 13.2, 5.4, "one deep safety", color=style.DEFENSE_DARK, fontsize=6.8)
plt.show()
Figure 59.8: Predict the play 1. 1984, second-and-6 near midfield. The 49ers are in split backs. The defense has walked its strong safety (SS, ringed) down beside the linebackers, so eight defenders stand within about five yards of the line, with one deep safety and both cornerbacks 7 to 8 yards off the receivers.

It’s 1984 and you’re calling plays for Walsh. Second-and-6 near midfield, split backs. The defense has brought a safety down: eight men near the line, one deep, and the cornerbacks playing well off your receivers. A 1970s offense runs here. What does the West Coast offense do, and where does the ball go?

It throws, short and outside, and treats the throw as the run, to the weak side. Count the box: eight defenders against seven blockers (five linemen, the tight end and a back) means a run is blocked one man short, and the defense built this alignment to stop it. But the same alignment leaves the edges thin, and one edge thinner than the other. The strong safety walked down on the tight end’s side, so that flat now has the Sam and the safety in it. On the other side, the Will is the only underneath defender over X and the halfback. So throw away from the rotation: the slant-flat to the weak side, exactly the play in Figure 59.2. The Will can’t cover both, the corner is 8 yards off and backing up, and the ball is out before the eight men crowded near the ball can get out to the edge. Second-and-6 only needs the six.

This is the core of the chapter in one decision: the West Coast offense doesn’t argue with eight in the box, it throws the ball to where the box isn’t. The defense’s counter is to keep eight in the box and press the corners, which leaves only one deep helper for the whole field; that’s when Walsh went over the top.

TipPredict the play 2: press man against the backs
Show the Python
dfn = era_43("one")
dfn = [q.moved(d=1.2, w=-13.0) if q.key == "LCB" else q for q in dfn]
dfn = [q.moved(d=1.2, w=13.0) if q.key == "RCB" else q for q in dfn]
dfn = [q.moved(d=3.4, w=6.6) if q.key == "SS" else q for q in dfn]
dfn = [q.moved(d=4.2, w=-3.8) if q.key == "WILL" else q for q in dfn]
dfn = [q.moved(d=4.2, w=3.6) if q.key == "SAM" else q for q in dfn]
dfn = [q.moved(d=6.0, w=0.0) if q.key == "MIKE" else q for q in dfn]
dfn = [q.moved(d=14.0) if q.key == "FS" else q for q in dfn]
p = Play(split_backs(), dfn, los=40, to_go=10)
p.man("WILL", "HB"); p.man("SAM", "FB"); p.man("SS", "Y")
p.zone((7.0, 0.0), (4.0, 7.0), color=style.DEFENSE, label="")
p.highlight("HB", "FB", "WILL", "SAM")
fld = draw(p, width_in=5.0, lateral=(-18.5, 18.0), window=(-7.0, 15.5), title=False)
say(fld, p, 3.4, -13.0, "press", color=style.DEFENSE_DARK, fontsize=6.8)
say(fld, p, 3.4, 13.0, "press", color=style.DEFENSE_DARK, fontsize=6.8)
point_to(fld, p, (10.4, -6.6), (4.2, -3.8), "linebackers\non the backs", color=style.DEFENSE_DARK, fontsize=6.8)
point_to(fld, p, (10.4, 6.6), (6.0, 0.0), "M: the 'rat',\nwaiting for crossers", color=style.DEFENSE_DARK,
         fontsize=6.8)
plt.show()
Figure 59.9: Predict the play 2. A physical defense in man coverage. Both cornerbacks press X and Z at the line, the strong safety is on the tight end, and two linebackers (W and S, ringed) are assigned to the two backs (H and F, ringed). The Mike (M) has no man: he sits in the middle as a ‘rat’, looking for anyone crossing his zone. One free safety stays deep.

You’re Walsh, and the defense is built to break your timing: press the wide receivers so they can’t reach their spots on schedule, and hit everybody. Your receivers will be jammed at the line, and the pressure will come fast. You have two ideas: a crossing route by X, to run him away from his press corner, or a route for one of the backs. Which matchup is better, and what route do you give him?

The backs, on option routes. Nobody can press a running back, because he starts five yards behind the line. And the men assigned to them are linebackers, who are bigger and slower than a back like Roger Craig. So give the back an option route: he runs straight at his linebacker, and breaks in or out, whichever way the linebacker doesn’t lean. (A flat or swing that makes the linebacker run to the sideline, or an angle route that starts toward the flat and snaps back inside, uses the same edge.)

The crossing route is the weaker idea here, and the defense drew it that way. Crossers are a good answer to man coverage in general, but X has to beat a press corner first, which costs time, and then run straight through the middle, where the Mike is sitting as a rat waiting for exactly that. The back’s route stays outside the rat’s area.

The trade-off is protection. If the backs run routes, they aren’t blocking, and a defense playing press man often blitzes. That’s what the check-release is for: each back looks for his blitzer first and releases only if nobody comes. Against a blitz the quarterback has to throw before the unblocked rusher arrives, which is why the quick throw to the back, or the hot route, is the first thing he looks at.

TipPredict the play 3: both options are gone
Show the Python
off3 = [q.moved(label={"SOL": "Z", "CLK": "X"}.get(q.key, q.label)) for q in CATCH_OFF]
dfn3 = [q.moved(label={"JONES": "E", "WALLS": "CB"}.get(q.key, q.label)) for q in CATCH_DFN]
p = Play(off3, dfn3, los=LOS_C, ball_y="right", to_go=None)
go_to(p, "QB", (-7.8, 18.0), kind="path", via=[(-4.6, 5.0), (-6.6, 11.0), (-7.6, 15.0)], speed=5.6)
go_to(p, "SOL", (6.8, 11.6), kind="route", via=[(5.6, 8.4)])
go_to(p, "CLK", (12.2, 4.4), kind="route", via=[(3.6, 12.2), (7.2, 8.4)])
go_to(p, "CLK", (14.6, 9.0), kind="route", via=[(14.3, 6.6)], delay=0.35)
go_to(p, "SS", (5.8, 10.6), kind="path", via=[(4.2, 9.0)])
go_to(p, "FS", (14.4, 12.6), kind="path")
go_to(p, "WALLS", (11.6, 6.4), kind="path", via=[(4.6, 11.4)])
go_to(p, "JONES", (-6.2, 15.4), kind="rush", via=[(-2.4, 8.4)])
p.highlight("SS", "FS")
fld = draw(p, width_in=5.2, lateral=(-11.0, 25.5), window=(-9.6, 16.0), title=False)
for xs_, ys_ in (((120.0, 120.0), (0.0, 160 / 3)), ((LOS_C - 10.0, 120.0), (0.0, 0.0))):   # end line, sideline
    (xa, ya), (xb, yb) = (fld.xy(x_, y_) for x_, y_ in zip(xs_, ys_))
    fld.ax.plot([float(xa), float(xb)], [float(ya), float(yb)], color=style.INK, lw=2.0, zorder=2)
num(fld, p, 7.8, 13.0, 1)
num(fld, p, 13.0, 7.6, 2)
say(fld, p, 4.0, 12.6, "jumped", color=style.DEFENSE_DARK, fontsize=6.8)
point_to(fld, p, (11.0, 19.0), (14.4, 12.6), "sitting on\nthe end line", color=style.DEFENSE_DARK, fontsize=6.8)
say(fld, p, -9.0, 15.4, "chased", color=style.DEFENSE_DARK, fontsize=6.8)
say(fld, p, 10.6, -6.0, "end zone", color=style.INK_2, fontsize=6.8)
say(fld, p, 14.6, -6.0, "end line", color=style.INK, fontsize=6.8)
plt.show()
Figure 59.10: Predict the play 3. Third-and-goal from the 6 (goal-to-go, so there’s no line to gain: the goal line is the line), 45 seconds left, one timeout, trailing by 6. The offense has called a sprint-out right: the slot receiver Z runs an out as the first option (1), and the wide receiver X runs across the end zone and back along the end line as the second (2). The strong safety (ringed) has jumped the out, and the free safety (ringed) has drifted to the back right of the end zone and is sitting on the end line, right where X will come back. The quarterback is being chased toward the sideline.

The same play as The Catch, on a different day, with one more thing gone wrong. The out to Z was jumped, and the free safety has read the play and parked himself at the back of the end zone, exactly where X is heading. It’s third-and-goal from the 6, 45 seconds left, one timeout, and you’re down by 6. Where should the ball go, and what’s the worst thing the quarterback can do?

Nowhere in the end zone: throw it away, out of bounds. The second option worked in The Catch because only one defender was near Clark, and the throw could go high, where only the receiver could reach it. Here a second defender is already standing on the spot, facing the quarterback, so a high throw to X is a jump ball with two defenders under it, or an interception. Both designed answers are covered, so the right play is the one the design keeps in reserve: throw the ball over the back corner, or over the sideline, where no one can catch it.

That isn’t giving up. An incompletion stops the clock and brings up fourth-and-goal from the 6 with about 40 seconds and a timeout left: one more full play, with a new call. The worst outcomes are the two that take that play away. An interception ends the game. A sack is nearly as bad: it loses yards, makes fourth down longer, and keeps the clock running, so the offense has to burn its last timeout just to get one more snap. This is the other half of the lesson of The Catch. Montana’s throw was placed so that it could only be a touchdown or an incompletion; when the touchdown isn’t there, take the incompletion.

Takeaways

  • Walsh’s problem was a team that couldn’t yet run the ball, and his answer was to make the pass do the run’s job. The horizontal passing game uses short, timed throws to the edges and across the field, caught by receivers who are already running, so the ball beats the defense’s pursuit and the tackle is made in space by a smaller man. Once it worked, the run came back: by 1981 the 49ers ran as often as the league did.
  • Timing, yards after the catch and ball placement were design goals, not happy accidents. Drops matched route depths and the quarterback’s feet ran his progression; routes caught receivers moving away from defenders; and the ball was thrown to the spot that let them keep running. The bill was an offense that needed an accurate quarterback and perfect rehearsal.
  • Scripting and Walsh’s practice methods were the system’s insurance. A timing offense can’t improvise, so he planned the week, the calls and the footwork in advance, and rehearsed even the backup options. The Catch was Sprint Right Option’s second option, practised all season.
  • The data still backs him. On early downs from 2023 to 2025, a pass thrown one to five yards past the line succeeded more often than a designed run, and a screen did about the same job as a run. The deeper throws are worth more per play, which says a modern Walsh would throw more, not less.
  • Defenses answered with press and physical play, the zone blitz, rally-and-tackle zones like Tampa 2, and by learning the language, and today’s two-high shells are the latest round.
  • The coaching tree is usually called the largest in football: ten of the 32 head coaches in 2025 and twelve in 2026 came from its branches. It grew because Walsh’s system was written down, explained and rehearsed, so it could be carried away. What travels down it is a way of working more than a playbook: in 2025 the tree’s teams threw short no more often than anyone else.

Connections

Builds on: 1978 and the Passing Revolution (the rules that made the timing game possible), Dropback Concepts (drive, curl-flat and how a concept is read), Passing Fundamentals and The Quick Game (timing, rhythm and the Bengals origin story), and Offensive Systems (the West Coast language).

Sets up: the rest of Part 12, starting with The Shanahan Tree (wide zone, boots and YAC over expected, Walsh’s offense by way of Denver) and Andy Reid and the Kansas City Chiefs (the Holmgren branch, gone spread); the Belichick case study, Game-Plan Football, is the other great system of the era, built on the opposite philosophy. Appendix E maps the coaching trees in full.

NoteGo deeper
  • Bill Walsh with Glenn Dickey, Building a Champion (1990): Walsh’s own account of rebuilding the 49ers, including his practice planning.
  • Bill Walsh with Brian Billick and James A. Peterson, Finding the Winning Edge (1997): the coaches’ manual: scripting, practice, the passing game and how a staff runs a season.
  • Bill Walsh with Steve Jamison and Craig Walsh, The Score Takes Care of Itself (2009): his leadership philosophy, built around what he called a “Standard of Performance.”
  • Tim Layden, Blood, Sweat and Chalk (2010): the chapter on the West Coast offense, with the coaches in their own words.
  • David Harris, The Genius: How Bill Walsh Reinvented Football and Created an NFL Dynasty (2008): the full biography.

  1. “The Catch (American football),” Wikipedia: https://en.wikipedia.org/wiki/The_Catch_(American_football) (January 10, 1982, Candlestick Park; Dallas led 27–21 after Doug Cosbie’s touchdown; the drive began at the 49ers’ 11 with 4:54 left and three timeouts, 89 yards, including Lenvil Elliott runs and a 14-yard reverse to Freddie Solomon; third-and-3 at the Dallas 6 with 58 seconds left; the 49ers’ second timeout before the snap; formation: split backs, Solomon in the right slot, Clark outside him; the call “Change Left Slot – Sprint Right Option”; Solomon the primary receiver, who had scored on the same play earlier in the game, slipped; Clark’s route diagonally left across the end zone and back right along the end line; Too Tall Jones, Larry Bethea and D.D. Lewis chasing Montana; the pump fake; Everson Walls covering Clark, with two interceptions and a fumble recovery earlier; the touchdown with 51 seconds left; Wersching’s extra point; Pillers’s sack and White’s fumble, recovered by Jim Stuckey; Clark’s view that it was a practised backup route, not a throwaway); San Francisco 49ers, “Celebrating the Anniversary of The Catch”: https://www.49ers.com/news/celebrating-the-anniversary-of-the-catch . Accounts differ on the number of plays: Wikipedia counts 14, while newspaper retrospectives (for example the San Francisco Chronicle, January 9, 2022: https://www.pressreader.com/usa/san-francisco-chronicle-late-edition-sunday/20220109/281925956365556) give 13, so this chapter gives no play count. Super Bowl XVI (January 24, 1982; 49ers 26, Bengals 21): see the Walsh note.↩︎

  2. “Bill Walsh (American football coach),” Wikipedia: https://en.wikipedia.org/wiki/Bill_Walsh_(American_football_coach) (born November 30, 1931; Raiders assistant 1966; Bengals assistant 1968–1975, resigning after Paul Brown named Bill “Tiger” Johnson his successor, and his claim that Brown “worked against my candidacy”; Chargers offensive coordinator 1976; Stanford 1977–78; 49ers head coach and general manager from 1979; season records 2–14 (1979), 6–10 (1980), 13–3 (1981), 15–1 (1984), 10–6 (1988); totals 92–59–1 in the regular season and 10–4 in the playoffs; Super Bowl XVI, 26–21 over Cincinnati, and XXIII, 20–16 over Cincinnati). Pro Football Hall of Fame, “Bill Walsh” (Class of 1993): https://www.profootballhof.com/players/bill-walsh/ . The 49ers’ 1978 record (2–14): “1978 San Francisco 49ers season,” Wikipedia: https://en.wikipedia.org/wiki/1978_San_Francisco_49ers_season↩︎

  3. Walsh to Tom FitzGerald of the San Francisco Chronicle, quoted in “Walsh, Bill,” Encyclopedia.com: https://www.encyclopedia.com/people/sports-and-games/sports-biographies/bill-walsh (the profile places the remark in its account of Walsh’s Bengals offense, built for the expansion team he joined in 1968; the bracketed “[opposing]” in that source’s version is dropped here).↩︎

  4. “1979 NFL draft,” Wikipedia: https://en.wikipedia.org/wiki/1979_NFL_draft (Joe Montana, Notre Dame, third round, 82nd overall); “Dwight Clark,” Wikipedia: https://en.wikipedia.org/wiki/Dwight_Clark (Clemson, tenth round, 249th overall; 6 ft 4 in).↩︎

  5. The same Encyclopedia.com profile describes his Bengals offense, the seed of the West Coast offense, as “passes so short they were effectively extended handoffs.” See the previous note.↩︎

  6. “West Coast offense,” Wikipedia, cited above: Walsh resented the label, “especially the word ‘finesse,’” and called Sports Illustrated’s Paul Zimmerman to protest that “that wasn’t his offense.”↩︎

  7. Pro Football Hall of Fame, “Jerry Rice” (Class of 2010; 22 touchdown catches in the strike-shortened 12-game 1987 season, then a record): https://www.profootballhof.com/players/jerry-rice/↩︎

  8. Team rushing attempts and pass attempts from NFL.com’s historical team statistics: rushing, https://www.nfl.com/stats/team-stats/offense/rushing/1979/reg/all (and the same page for 1981 and 1984), and passing, https://www.nfl.com/stats/team-stats/offense/passing/1979/reg/all (and 1981, 1984). Run share is rushes divided by rushes plus pass attempts (sacks excluded), a course calculation: 49ers 480 of 1,082 in 1979 (44%; league 54%), 560 of 1,077 in 1981 (52%; league 51%), 534 of 1,030 in 1984 (52%; league 49%); the 49ers’ 602 pass attempts in 1979 were the most in the league. Rushes include some quarterback runs and scrambles, which the old tables don’t separate. Wendell Tyler’s team-record 1,262 rushing yards in 1984: “1984 San Francisco 49ers season,” Wikipedia: https://en.wikipedia.org/wiki/1984_San_Francisco_49ers_season .↩︎

  9. “West Coast offense,” Wikipedia: https://en.wikipedia.org/wiki/West_Coast_offense (“originally the offense used two split backs,” with five players on one side of the formation and four on the other; short, high-percentage passing; the quarterback’s three reads before checking down to a back or tight end).↩︎

  10. Career totals (1,549 receptions, 22,895 receiving yards, 208 total touchdowns, all NFL records) as listed in “West Coast offense,” Wikipedia, cited above; drafted in the first round, 16th overall, in 1985, from Mississippi Valley State: “1985 NFL draft,” Wikipedia: https://en.wikipedia.org/wiki/1985_NFL_draft↩︎

  11. “Roger Craig (American football),” Wikipedia: https://en.wikipedia.org/wiki/Roger_Craig_(American_football) (second round, 49th overall, in the 1983 draft from Nebraska; 1,050 rushing and 1,016 receiving yards in 1985, “the first in NFL history to run and receive for at least 1,000 yards in the same season,” with a league-leading 92 receptions).↩︎

  12. The AP obituary (July 30, 2007) credits Walsh with popularizing “the practice of scripting the first 15 offensive plays of a game”: https://www.nfl.com/news/hall-of-fame-coach-bill-walsh-who-won-three-super-bowls-with-th-09000d5d800879e1 ; “West Coast offense,” Wikipedia, cited above, says he sometimes scripted up to 25.↩︎

  13. Richard J. Cattani, “Walsh on Football: The Corporate Game,” Christian Science Monitor (November 8, 1990), a review of Walsh’s Building a Champion: https://www.csmonitor.com/1990/1108/ecatt08.html (“everything was scripted”; “how many steps the quarterback takes”; “Plays were practiced until they became automatic”; options prepared for “third down and four at the goal line”; “Walsh counted out the days of the training camp and season”; “Four different practice formats,” from “a grueling two-and-a-half-hour practice to a light, thirty-minute practice the day before a game,” “never two hard sessions in a row”; Walsh: “It’s vitally important that players take the field to learn something each session”; he “coached the coaches”).↩︎

  14. The full call is taken from the 49ers’ own 1982 offensive playbook (p. 325), as cited in “The Catch (American football),” Wikipedia, cited above; the playbook is reproduced at Football XO’s: https://footballxos.com/download/offense/pro-offense/1982-San-Francisco-49ers-West-Coast-Offense-Bill-Walsh.pdf . Clark’s own retellings and the Associated Press obituary of Clark (Josh Dubow, June 4, 2018: https://foxbaltimore.com/news/nation-world/former-49ers-wide-receiver-dwight-clark-dead-at-61) use the short name, “Sprint Right Option,” called by Walsh. The formation (split backs, Solomon in the right slot, Clark wide right) is from the Wikipedia article.↩︎

  15. “Flex defense,” Wikipedia: https://en.wikipedia.org/wiki/Flex_defense (developed by Tom Landry; in the classic version two of the four linemen were “flexed two feet off the line of scrimmage”); the course’s history of the Flex is in From the Single Wing to the 1970s Scoring Drought. Which linemen Dallas set back on this snap isn’t known from the sources used here; the figure shows a typical version.↩︎

  16. “Bill Walsh (American football coach),” Wikipedia, cited above (records for 1981, 1984 and 1988; Super Bowls XVI and XXIII); “Super Bowl XIX,” Wikipedia: https://en.wikipedia.org/wiki/Super_Bowl_XIX (49ers 38, Dolphins 16); “Super Bowl XXIV,” Wikipedia: https://en.wikipedia.org/wiki/Super_Bowl_XXIV (49ers 55, Broncos 10); “Super Bowl XXIX,” Wikipedia: https://en.wikipedia.org/wiki/Super_Bowl_XXIX (49ers 49, Chargers 26; Seifert head coach, Mike Shanahan offensive coordinator); 1989 and 1994 regular-season records (14–2, 13–3): “George Seifert,” Wikipedia: https://en.wikipedia.org/wiki/George_Seifert (49ers defensive coordinator 1983–1988, head coach 1989–1996).↩︎

  17. “Sam Wyche,” Wikipedia: https://en.wikipedia.org/wiki/Sam_Wyche (49ers assistant who “directed the passing game,” 1979–1982; Bengals head coach 1984–1991; his Bengals “the first to use the no-huddle/hurry-up offense as a base offense”); “Super Bowl XXIII,” Wikipedia: https://en.wikipedia.org/wiki/Super_Bowl_XXIII (January 22, 1989; 49ers 20, Bengals 16, Montana’s touchdown pass to John Taylor with 34 seconds left).↩︎

  18. “Super Bowl XIX,” Wikipedia, cited above (January 20, 1985, Stanford Stadium; Montana the MVP; Roger Craig’s three touchdowns, two receiving, from 8 and 16 yards, and one rushing; the first player to score three touchdowns in a Super Bowl; Dan Marino’s 1984 records of 5,084 passing yards and 48 touchdown passes).↩︎

  19. “America’s Game: The Super Bowl Champions,” Wikipedia: https://en.wikipedia.org/wiki/America%27s_Game:_The_Super_Bowl_Champions (NFL Films and NFL Network; episodes on the 1981, 1984, 1988 and 1989 49ers, first aired between January and March 2007, in a series that began airing in November 2006).↩︎

  20. “Super Bowl XXIV,” Wikipedia, cited above (January 28, 1990, Louisiana Superdome; Montana’s five touchdown passes, then a Super Bowl record, three of them to Rice; the 45-point margin the largest in Super Bowl history, still the record through Super Bowl LX: see planning/FACTS-current.md, §1, for the recent results).↩︎

  21. Pro Football Hall of Fame, “Joe Montana” (Class of 2000): https://www.profootballhof.com/players/joe-montana/ ; “Joe Montana,” Wikipedia: https://en.wikipedia.org/wiki/Joe_Montana . The Wikipedia article lists his completion-percentage titles as 1980, 1981, 1985, 1987 and 1989, and calls his 112.4 rating in 1989 “the highest single-season passer rating in NFL history” at the time (Steve Young passed it in 1994). Pro Football Reference’s season pages carry the same leaders but block automated fetches.↩︎

  22. “Giants–49ers rivalry,” Wikipedia: https://en.wikipedia.org/wiki/Giants%E2%80%9349ers_rivalry (the Giants’ 17–3 wild-card win after the 1985 season and 49–3 divisional win after the 1986 season, in which Jim Burt’s hit knocked Montana out of the game with a concussion as he threw the pass that Lawrence Taylor returned for a touchdown; that game was played January 4, 1987).↩︎

  23. “West Coast offense,” Wikipedia, cited above (Bill Parcells reportedly asking “What do you think of that West Coast offense now?” after the Giants’ 17–3 playoff win over the 49ers in the 1985 season; Bernie Kosar’s 1993 description of the Dallas Cowboys’ offense as a “West Coast offense,” meaning the Air Coryell system, publicized by Paul Zimmerman of Sports Illustrated, after which “a reporter mistakenly applied Kosar’s statement” to Walsh’s 49ers; Walsh’s objection). The two stories are presented here as competing accounts; neither is settled. The course’s Offensive Systems chapter gives the short version.↩︎

  24. “Dick LeBeau,” Wikipedia: https://en.wikipedia.org/wiki/Dick_LeBeau (Bengals defensive coordinator 1984–1991); NFL.com, “NFL 100 Greatest Game Changers: Dick LeBeau/Zone Blitz”: https://nfl.com/100/originals/100-greatest/game-changers-60 (in 1984 the Bengals’ new defensive coordinator began adapting their four-linebacker package “in an effort to stop the emerging West Coast offense”); “Zone blitz has come a long way since the advent of the 3-4,” NFL.com: https://nfl.com/news/zone-blitz-has-come-a-long-way-since-the-advent-of-the-3-4-09000d5d81e01d8e .↩︎

  25. “Super Bowl XXXVII,” Wikipedia: https://en.wikipedia.org/wiki/Super_Bowl_XXXVII (January 26, 2003; Buccaneers 48, Raiders 21; Gruden had coached the Raiders through the 2001 season; Rich Gannon’s five interceptions, a Super Bowl record). The Raiders’ side: Josh Schrock, “Raiders claim Jon Gruden, Buccaneers knew their plays in Super Bowl XXXVII,” NBC Sports Bay Area, July 10, 2020: https://www.nbcsportsbayarea.com/nfl/raiders-claim-jon-gruden-buccaneers-knew-their-plays-in-super-bowl-xxxvii/1325862 (Lincoln Kennedy: “Every level of the defense knew what we were doing”; Sam Adams recalling Buccaneers telling him they “cannot believe you’re using the same checks, and the same terminology”). The Buccaneers’ side: Josh Schrock, “How Gruden prepared Bucs to blow out Raiders in Super Bowl,” NBC Sports Bay Area, November 22, 2020: https://www.nbcsportsbayarea.com/nfl/raiders/how-gruden-prepared-bucs-to-blow-out-raiders-in-super-bowl/1127303 (Derrick Brooks). Tim Brown has blamed Bill Callahan’s late change of game plan instead: ESPN, https://www.espn.com/nfl/story/_/id/8868196/tim-brown-bill-callahan-sabotaged-oakland-raiders-super-bowl-xxxvii-loss .↩︎

  26. The page number is from the 1982 49ers playbook cited for the call above (p. 325). Bill Walsh, with Brian Billick and James A. Peterson, Finding the Winning Edge (Champaign, IL: Sports Publishing, 1997), listed under “Go deeper.”↩︎

  27. “West Coast offense,” Wikipedia, cited above (Holmgren as Walsh’s quarterbacks coach 1986–1988 and Seifert’s offensive coordinator 1989–1991, his Packers 1992–1998 and Super Bowl XXXI; Gruden, Reid, Harbaugh as an Eagles assistant 1999–2007, Pederson). Childress, Nagy and Mariucci’s staff histories: “Brad Childress,” Wikipedia: https://en.wikipedia.org/wiki/Brad_Childress (Reid’s Eagles quarterbacks coach 1999–2001 and offensive coordinator 2002–2005); “Matt Nagy,” Wikipedia; “Steve Mariucci,” Wikipedia: https://en.wikipedia.org/wiki/Steve_Mariucci (Holmgren’s Packers quarterbacks coach 1992–1995). The head-coaching years in the tree figure are from each coach’s Wikipedia article (for example Gruden: Raiders 1998–2001 and 2018–2021, Buccaneers 2002–2008; Gary Kubiak: Texans 2006–2013, Broncos 2015–2016; McCarthy: Packers 2006–2018, Cowboys 2020–2024) and, for 2026, from the course fact sheet (planning/FACTS-current.md, §9).↩︎

  28. “West Coast offense,” Wikipedia, cited above (Paul Hackett, 49ers assistant 1983–1985; taught the system to Mike McCarthy; McCarthy’s Super Bowl XLV with the Packers); Green Bay Packers, “Mike McCarthy named Green Bay’s 14th head coach” (2006), on his years under Hackett at Pittsburgh (1989–92) and as Kansas City’s quarterbacks coach (1995–98): https://www.packers.com/news/mike-mccarthy-named-green-bay-s-14th-head-coach-2452545 . McCarthy as Steelers head coach in 2026: course fact sheet (planning/FACTS-current.md, §9), from https://en.wikipedia.org/wiki/2026_NFL_season .↩︎

  29. “Brian Billick,” Wikipedia: https://en.wikipedia.org/wiki/Brian_Billick (Dennis Green’s assistant at Stanford 1989–1991; Vikings tight ends coach and then offensive coordinator 1994–1998 under Green; Ravens head coach 1999–2007; Super Bowl XXXV, 34–7 over the Giants after the 2000 season). Billick also worked for the 49ers in public relations in 1979–80, not as a coach.↩︎

  30. “Super Bowl XXXII,” Wikipedia: https://en.wikipedia.org/wiki/Super_Bowl_XXXII (January 25, 1998; Broncos 31, Packers 24; Mike Shanahan and Mike Holmgren head coaches). Mike Shanahan as the 49ers’ offensive coordinator 1992–1994: “Mike Shanahan,” Wikipedia: https://en.wikipedia.org/wiki/Mike_Shanahan . McVay’s first NFL job on Gruden’s Tampa Bay staff (2008): “Sean McVay,” Wikipedia: https://en.wikipedia.org/wiki/Sean_McVay . Staff links in the tree figure: “Kyle Shanahan,” Wikipedia: https://en.wikipedia.org/wiki/Kyle_Shanahan (offensive quality control on Gruden’s Tampa Bay staff 2004–2005; Houston under Gary Kubiak 2006–2009; Washington offensive coordinator under his father 2010–2013); “Kevin Stefanski,” Wikipedia: https://en.wikipedia.org/wiki/Kevin_Stefanski (joined the Vikings in 2006 “as an assistant to then-head coach Brad Childress”); “Liam Coen,” Wikipedia: https://en.wikipedia.org/wiki/Liam_Coen (McVay’s Rams 2018–2020 and offensive coordinator 2022; Jaguars head coach from 2025).↩︎

  31. “Sean McVay,” Wikipedia: https://en.wikipedia.org/wiki/Sean_McVay (assistant wide receivers coach on Jon Gruden’s Buccaneers staff, 2008; Washington assistant tight ends coach 2010 and tight ends coach 2011–2013 under head coach Mike Shanahan, with Kyle Shanahan as offensive coordinator; Washington offensive coordinator 2014–2016 under Jay Gruden).↩︎

  32. Course fact sheet (planning/FACTS-current.md, §§1 and 9, checked October 2026), from https://en.wikipedia.org/wiki/2026_NFL_season (head coaches), https://www.profootballrumors.com/2026/02/2026-nfl-offensive-defensive-coordinator-search-tracker (coordinators), https://kogo.iheart.com/content/2026-01-20-mike-mcdaniel-lands-nfl-coaching-gig-report (McDaniel to the Chargers) and https://en.wikipedia.org/wiki/Super_Bowl_LX (Seattle’s staff: Macdonald, Kubiak, Durde). Macdonald’s years on Harbaugh’s Baltimore staff: “Mike Macdonald (American football),” Wikipedia: https://en.wikipedia.org/wiki/Mike_Macdonald_(American_football) . Coen still the Jaguars’ head coach in 2026: Jacksonville Jaguars, “Day after Week 1 win over Browns”: https://www.jaguars.com/news/day-after-week-1-win-over-browns-coen-pleased-with-fast-week-1-start ; Zac Taylor retained by Cincinnati for 2026: FOX19, “Zac Taylor, Duke Tobin will be back for 2026 season” (January 5, 2026): https://www.fox19.com/2026/01/05/zac-taylor-duke-tobin-will-be-back-2026-season/ . The 2025 list counts head coaches who started the 2025 season.↩︎

  33. “John Harbaugh,” Wikipedia: https://en.wikipedia.org/wiki/John_Harbaugh (Eagles special teams coordinator 1998–2006 and defensive backs coach 2007, before becoming the Ravens’ head coach in 2008).↩︎

  34. San Francisco 49ers, “A look into the history and diversity of Bill Walsh’s coaching tree”: https://49ers.com/news/a-look-into-the-history-and-diversity-of-bill-walsh-s-coaching-tree (dates the formal fellowship to 1986, begun with Dr. Harry Edwards and Rodney Knox; Walsh hiring Dennis Green in 1979 and giving Ray Rhodes his first NFL job; every team now participates); San Francisco 49ers, “NFL re-names program after Walsh” (January 28, 2009): https://www.49ers.com/news/nfl-re-names-program-after-walsh-491253 (the idea “introduced to the league by Walsh in 1987”; Herm Edwards, Marvin Lewis, Lovie Smith, Mike Tomlin and Raheem Morris as graduates who became head coaches). Edwards as the first graduate named an NFL head coach (Jets, 2001): New England Patriots (NFL release), “Bengals’ Lewis joins Jets’ Edwards as head coach grads of program”: https://www.patriots.com/news/nfl-minority-coaching-fellowship-attracts-81-participants-170256 .↩︎

  35. “Christian McCaffrey,” Wikipedia: https://en.wikipedia.org/wiki/Christian_McCaffrey (2019 with Carolina: 1,387 rushing and 1,005 receiving yards, the third player with 1,000 of each in one season after Roger Craig in 1985 and Marshall Faulk in 1999); 2025 Comeback Player of the Year with San Francisco: course fact sheet (planning/FACTS-current.md, §2).↩︎

  36. NFL Next Gen Stats via NFL.com, “NFL trend watch: Heavy personnel could shape Super Bowl LX showdown between Patriots and Seahawks”: https://www.nfl.com/news/nfl-trend-watch-heavy-personnel-could-shape-super-bowl-lx-showdown-between-patriots-and-seahawks (fewer than three wide receivers on 41.7% of plays in 2025, the most since 2016); under-center share from FTN charting (qb_location == "U"): 27.3% in 2023, 33.6% in 2025, course calculation in planning/FACTS-current.md, §10.↩︎