Dropback Concepts: Smash, Flood, Mesh, Levels, Dagger, and Four Verticals

The two- and three-receiver combinations behind almost every pass thrown more than ten yards, and how a quarterback reads each one

Show the Python (shared drawing helpers for this chapter)
import os
import logging
import shutil

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.patches import Circle, Ellipse, FancyArrowPatch, Polygon, RegularPolygon, Patch, Rectangle
from matplotlib.lines import Line2D

import gridiron.play as gplay
from contextlib import contextmanager
from gridiron import Play, Player, Field, routes as r, style

logging.getLogger("matplotlib.font_manager").setLevel(logging.ERROR)
style.apply()
PDF = os.environ.get("RTG_FORMAT", "").lower() in ("pdf", "typst", "print")
READ_GREEN = "#0f7a55"   # a darker step of the aqua highlight, for text, numbers and read lines
GREY = style.MUTED
OD, DD = style.OFFENSE_DARK, style.DEFENSE_DARK
LINE_D, OFF_BALL, GUN, UNDER = -0.7, -1.5, -5.0, -1.9
LOS = 40  # the offense's own 30: room to draw deep routes


# ---------------------------------------------------------------- players and alignments
def O(key, pos, d, w, label=None):
    return Player(key, pos, "off", d, w, label=key if label is None else label)


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


def line5():
    """The five offensive linemen, 1.6 yards apart (only the center is lettered)."""
    return [O(k, k, LINE_D, w, "C" if k == "C" else "") for k, w in
            (("LT", -3.2), ("LG", -1.6), ("C", 0.0), ("RG", 1.6), ("RT", 3.2))]


def spread(rb_w=-1.5, x_w=-15.0, h_w=-9.0, y_w=9.0, z_w=15.0):
    """11 personnel, shotgun 2x2. X and Z on the line (with the five linemen: seven on the line);
    H in the left slot and Y, a tight end split out, in the right slot, both a step off the ball."""
    return line5() + [O("QB", "QB", GUN, 0.0, "Q"), O("RB", "RB", GUN, rb_w, "R"),
                      O("X", "WR", LINE_D, x_w), O("H", "WR", OFF_BALL, h_w),
                      O("Y", "TE", OFF_BALL, y_w), O("Z", "WR", LINE_D, z_w)]


def dline(s=1):
    return [D("WDE", "DE", 1.0, -5.4 * s, "E"), D("WDT", "DT", 1.0, -0.8 * s, "T"),
            D("SDT", "DT", 1.0, 2.4 * s, "T"), D("SDE", "DE", 1.0, 5.4 * s, "E")]


def _apply(players, extra):
    extra = extra or {}
    return [q.moved(d=extra[q.key][0], w=extra[q.key][1]) if q.key in extra else q for q in players]


def cover3(nb_side=1, extra=None):
    """4-2-5 nickel, Cover 3 against 2x2: corners off with outside leverage, one deep safety, and a
    curl-flat defender over each slot: the nickel ($) on side nb_side (+1 = offense's right), the
    strong safety on the other."""
    s = nb_side
    return _apply(dline() + [D("MIKE", "MIKE", 4.5, 1.8, "M"), D("WILL", "WILL", 4.5, -2.2, "W"),
                             D("LCB", "CB", 7.0, -16.0, "CB"), D("RCB", "CB", 7.0, 16.0, "CB"),
                             D("NB", "NB", 5.0, s * 7.2, "$"), D("SS", "SS", 5.5, -s * 7.2, "SS"),
                             D("FS", "FS", 13.0, 0.0, "FS")], extra)


def cover2(extra=None):
    """4-2-5 nickel, Cover 2: corners squatting at 5 yards with outside leverage, two deep safeties."""
    return _apply(dline() + [D("MIKE", "MIKE", 4.5, 1.6, "M"), D("WILL", "WILL", 4.5, -4.2, "W"),
                             D("NB", "NB", 4.5, 6.6, "$"),
                             D("LCB", "CB", 5.0, -16.3, "CB"), D("RCB", "CB", 5.0, 16.3, "CB"),
                             D("FS", "FS", 12.5, -8.5, "FS"), D("SS", "SS", 12.5, 8.5, "SS")], extra)


def quarters(extra=None):
    """4-2-5 nickel, Quarters (Cover 4): corners off, two safeties at about 10 yards reading the slots."""
    return _apply(dline() + [D("MIKE", "MIKE", 4.5, 1.6, "M"), D("WILL", "WILL", 4.5, -4.2, "W"),
                             D("NB", "NB", 4.5, 6.6, "$"),
                             D("LCB", "CB", 7.5, -16.0, "CB"), D("RCB", "CB", 7.5, 16.0, "CB"),
                             D("FS", "FS", 10.0, -7.5, "FS"), D("SS", "SS", 10.0, 7.5, "SS")], extra)


def cover1(extra=None):
    """Nickel Cover 1 (man-free): corners pressed, the nickel on H, the strong safety on Y, the Will
    on the back, the Mike free to rush or sit in the hole, one deep safety."""
    return _apply(dline() + [D("MIKE", "MIKE", 4.2, -1.8, "M"), D("WILL", "WILL", 4.2, 1.4, "W"),
                             D("NB", "NB", 1.6, -8.3, "$"), D("SS", "SS", 4.5, 8.0, "SS"),
                             D("LCB", "CB", 1.3, -14.3, "CB"), D("RCB", "CB", 1.3, 14.3, "CB"),
                             D("FS", "FS", 13.5, 0.0, "FS")], extra)


def protect(p, rushers=("WDE", "WDT", "SDT", "SDE"), depth=1.2, rush=1.6, linemen=("LT", "LG", "C", "RG", "RT")):
    """Linemen set, four rushers engage them: enough to say 'pass', no more."""
    for k in linemen:
        p.block(k, pts=[(-depth, 0.0)])
    for k in rushers:
        if k in p.players:
            p.rush(k, pts=[(-rush, 0.0)], speed=2.0)


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


def route_to(p, key, *pts, delay=0.0, speed=None):
    """A route through absolute (d, w) points."""
    return go_to(p, key, pts[-1], kind="route", via=pts[:-1], delay=delay, speed=speed)


SWING_NUM = {-1: (-4.4, -14.6), 1: (-4.4, 14.6)}


def swing(p, side=-1, key="RB"):
    """The back checks for a blitzer, then swings wide behind the slot: the checkdown.
    Returns where to put his progression number."""
    route_to(p, key, (-4.8, side * 4.5), (-4.2, side * 10.5), (-3.0, side * 13.0), delay=0.4)
    return SWING_NUM[side]


# ---------------------------------------------------------------- drawing helpers
def _P(fld, p, d, w):
    return fld.xy(*p.to_abs(d, w))


def clean(ax):
    """Drop the painted yard numbers (they sit where receivers stand); clip all text to the window."""
    for t in list(ax.texts):
        if t.get_text().isdigit() and t.get_alpha() == 0.55:
            t.remove()
        else:
            t.set_clip_on(True)


def ruler(fld, p, fs=6.0, step=5, top=30):
    """Depth labels (yards past the line of scrimmage) just inside the left edge of the window."""
    xl = (160 / 3) - fld.y1 + 0.4
    for d in range(step, top + 1, step):
        y = p.los + d
        if fld.x0 + 1.5 <= y <= fld.x1 - 1.5:
            fld.ax.text(xl, y, f"{d}" + (" yd" if d == step 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 draw(p, width_in=8.3, ax=None, show_ruler=True, **kw):
    """Draw a play without a title. A stand-alone field gets ~6.4 in of drawing width after the
    default subplot margins are cropped (width_in is the figure width before cropping)."""
    fld = p.draw(width_in=width_in, ax=ax, title=False, **kw)
    clean(fld.ax)
    if show_ruler:
        ruler(fld, p)
    return fld


def say(fld, p, d, w, text, color=style.INK_2, fontsize=7.0, ha="center", weight="normal", box=True):
    x, y = _P(fld, p, d, w)
    fld.ax.text(float(x), float(y), text, fontsize=fontsize, color=color, ha=ha, va="center", zorder=13,
                fontweight=weight, linespacing=1.15,
                bbox=dict(boxstyle="round,pad=0.18", fc=style.SURFACE, ec="none", alpha=0.9) if box else None)


def point_to(fld, p, text_at, target, text, color=style.INK_2, fontsize=7, ha="center"):
    (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=ha, va="center", zorder=13, linespacing=1.15,
                    arrowprops=dict(arrowstyle="-", color=color, lw=0.7, shrinkA=2, shrinkB=7),
                    bbox=dict(boxstyle="round,pad=0.18", fc=style.SURFACE, ec="none", alpha=0.9))


def branch(fld, p, pts, color=READ_GREEN, lw=1.7, dash=(0, (3, 2.2)), end="arrow", z=5):
    """A dashed path through absolute (d, w) points: a defender's reaction, or the other branch of a read."""
    xs, ys = _P(fld, p, np.array([q[0] for q in pts]), np.array([q[1] for q in pts]))
    fld.ax.plot(xs, ys, color=color, lw=lw, ls=dash, zorder=z, solid_capstyle="round")
    if end == "arrow":
        fld.ax.add_patch(FancyArrowPatch((xs[-2], ys[-2]), (xs[-1], ys[-1]), arrowstyle="-|>",
                                         mutation_scale=10, color=color, lw=lw, zorder=z))


def ghost(fld, p, pts):
    """A route that is part of the call but not part of the read: drawn faint grey."""
    branch(fld, p, pts, color="#a9a8a1", lw=1.4, dash="-", z=3.6)


def taken(fld, p, pts, lw=1.4):
    """A route the defender has taken away: grey, dashed, with an X at the end."""
    branch(fld, p, pts, color=GREY, lw=lw, dash=(0, (2, 2)), end="none")
    x, y = _P(fld, p, *pts[-1])
    fld.ax.plot([float(x)], [float(y)], marker="X", ms=8, color=DD, mec="white", mew=0.7, zorder=9)


def opening(fld, p, center, size, color=style.THIRD):
    """A soft green oval: the throwing window that opens."""
    x, y = _P(fld, p, *center)
    fld.ax.add_patch(Ellipse((float(x), float(y)), size[1], size[0], fc=color, alpha=0.2, ec=color, lw=1.0,
                             zorder=1.6))


def triangle(fld, p, pts, color=style.THIRD):
    xy = [tuple(float(v) for v in _P(fld, p, *q)) for q in pts]
    fld.ax.add_patch(Polygon(xy, closed=True, fc=color, alpha=0.15, ec=color, lw=1.3, ls=(0, (4, 2)), zorder=1.7))


def bracket(fld, p, d, w0, w1, text, color=style.INK_2, fontsize=7, dy=1.2):
    """A horizontal dimension line at depth d from w0 to w1, labelled above."""
    (x0, y0), (x1, y1) = _P(fld, p, d, w0), _P(fld, p, d, w1)
    fld.ax.annotate("", (float(x1), float(y1)), xytext=(float(x0), float(y0)),
                    arrowprops=dict(arrowstyle="<->", color=color, lw=0.8), zorder=6)
    if text:
        say(fld, p, d + dy, (w0 + w1) / 2, text, color=color, fontsize=fontsize)


def num(fld, p, n, at, color=READ_GREEN, fs=7.6):
    """A progression number: the order of the quarterback's looks."""
    x, y = _P(fld, p, *at)
    fld.ax.text(float(x), float(y), str(n), ha="center", va="center", fontsize=fs, color="white",
                fontweight="bold", zorder=14,
                bbox=dict(boxstyle="circle,pad=0.22", fc=color, ec="white", lw=0.8))


def tag(fld, p, text, fontsize=7.4):
    """The stretch label, top right of a plate."""
    fld.ax.text(0.985, 0.975, text, transform=fld.ax.transAxes, ha="right", va="top", fontsize=fontsize,
                color=style.INK, fontweight="bold", zorder=15,
                bbox=dict(boxstyle="round,pad=0.3", fc=style.SURFACE, ec=style.BASELINE, lw=0.6))


def panel_title(ax, text, fs=9):
    ax.set_title(text, fontsize=fs, loc="left", color=style.INK, fontweight="bold")


LEGEND = {
    "route": Line2D([], [], color=style.OFFENSE, lw=1.8, label="route"),
    "ghost": Line2D([], [], color="#a9a8a1", lw=1.4, label="route outside the read"),
    "block": Line2D([], [], color=style.OFFENSE_DARK, lw=1.8, marker="|", ms=8, mew=2.2, label="block"),
    "run": Line2D([], [], color=style.OFFENSE_DARK, lw=2.6, label="ball carrier / fake"),
    "pass": Line2D([], [], color=style.BALL, lw=1.3, ls=(0, (2, 2)), label="the throw"),
    "drop": Line2D([], [], color=style.DEFENSE, lw=1.3, ls=(0, (3, 2)), label="defender's drop"),
    "man": Line2D([], [], color=style.DEFENSE, lw=0.9, ls=(0, (1, 2)), label="man assignment"),
    "react": Line2D([], [], color=style.DEFENSE, lw=1.4, ls=(0, (3, 2.2)), label="defender's reaction"),
    "num": Line2D([], [], marker="o", ls="", ms=9, mfc=READ_GREEN, mec="white", label="progression (1 = first look)"),
    "ring": Line2D([], [], marker="o", ls="", ms=11, mfc="none", mec=style.THIRD, mew=2, label="defender in conflict"),
    "window": Patch(fc=style.THIRD, alpha=0.25, ec=style.THIRD, label="window that opens"),
    "taken": Line2D([], [], color=GREY, lw=1.4, ls=(0, (2, 2)), marker="X", ms=7, mfc=DD,
                    mec="white", label="taken away"),
    "ltg": Line2D([], [], color=style.LINE_TO_GAIN, lw=2.2, label="line to gain"),
}


def legend(fig, keys, ncol=None, y=0.0, fs=7):
    hs = [LEGEND[k] for k in keys]
    fig.legend(handles=hs, loc="upper center", bbox_to_anchor=(0.5, y), ncol=ncol or min(len(hs), 4), fontsize=fs,
               frameon=False, handlelength=2.0, columnspacing=1.0)


PLATE_KEYS = ["route", "ghost", "drop", "num", "ring", "ltg"]


def field_legend(ax, keys, fs=7.2):
    """Legend tucked just under a single field."""
    hs = [LEGEND[k] for k in keys]
    ax.legend(handles=hs, loc="upper center", bbox_to_anchor=(0.5, 0.0), ncol=len(hs) if len(hs) <= 4 else 3, fontsize=fs,
              frameon=False, handlelength=2.0, columnspacing=1.2, borderaxespad=0.2)


def plate(p, nums=(), ghosts=(), stretch=None, window=(-8.5, 26.5), lateral="full", keys=None, width_in=6.9):
    """A concept plate: the call, the read and the progression, in one consistent style."""
    fld = draw(p, lateral=lateral, window=window, width_in=width_in)
    for pts in ghosts:
        ghost(fld, p, pts)
    for n, at in nums:
        num(fld, p, n, at)
    if stretch:
        tag(fld, p, stretch)
    field_legend(fld.ax, keys or PLATE_KEYS)
    return fld


# ---------------------------------------------------------------- frame strips (print) and video (web)
MARK_R = 0.7


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


def frame_panel(ax, play, df, t, lateral, window_, rings=(), throw_t=None, deco=None):
    """One moment of a simulated play, with faint trails, optional rings and the ball's flight so far."""
    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=True,
                lateral=(ylo, yhi), to_go=play.to_go)
    cur = df[df["frameId"] == fid]
    shown = cur[cur["x"].between(play.los + window_[0] + 0.4, play.los + window_[1] - 0.4)
                & cur["y"].between(ylo + 0.4, yhi - 0.4)]
    ids = set(shown["_id"])
    trails = df[(df["frameId"] <= fid) & df["_id"].isin(ids) & (df["side"] != "ball")]
    for (_, side), g in trails.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.45, zorder=3)
    if throw_t is not None and t > throw_t:
        b = df[(df["side"] == "ball") & (df["frameId"] <= fid)]
        b = b[b["time"] >= throw_t - 0.05]
        xs, ys = fld.xy(b["x"].values, b["y"].values)
        ax.plot(xs, ys, color=style.BALL, lw=1.3, ls=(0, (2, 1.5)), zorder=9)
    ppy = fld.pts_per_yard()
    for _, rr in shown.iterrows():
        x, y = (float(v) for v in fld.xy(rr["x"], rr["y"]))
        if rr["side"] == "ball":
            if throw_t is None or t <= throw_t:
                # still in the quarterback's hands: draw it at his throwing shoulder so his letter stays readable
                x, y = (float(v) for v in fld.xy(rr["x"], rr["y"] - 1.0))
            elif rr["x"] > play.los + 2:
                # in flight or caught: nudge it beside the receiver so his letter stays readable
                x, y = (float(v) for v in fld.xy(rr["x"] - 0.9, rr["y"] - 0.9))
            ax.add_patch(Ellipse((x, y), 1.0, 0.62, fc=style.BALL, ec="white", lw=0.5, zorder=11))
            continue
        lab = str(rr.get("label", "") or "")
        if rr["side"] == "off":
            ax.add_patch(Circle((x, y), MARK_R, fc=style.OFFENSE, ec="white", lw=0.6, zorder=8))
            tc = "white"
        else:
            ax.add_patch(RegularPolygon((x, y), 4, radius=MARK_R * 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 * (0.85 if len(lab) <= 1 else 0.62)))
        ax.text(x, y, lab, ha="center", va="center", fontsize=fs, color=tc, fontweight="bold", zorder=10)
    for key, col in rings:
        rr = cur[cur["displayName"] == key].iloc[0]
        x, y = fld.xy(rr["x"], rr["y"])
        ax.add_patch(Circle((float(x), float(y)), MARK_R * 1.6, fc="none", ec=col, lw=1.7, zorder=7))
    if deco is not None:
        deco(ax, fld, cur, t)
    return fld


def strip(play, times, notes=None, lateral=(-24, 4), window=(-8, 14), ncols=2, rings=(), throw_t=None,
          width_in=6.5, deco=None):
    df = tracking(play)
    nrows = int(np.ceil(len(times) / ncols))
    pw = width_in / ncols
    span_w, span_d = abs(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 + (0.6 if notes else 0.25))))
    for ax in axes.flat[len(times):]:
        ax.axis("off")
    for i, (ax, t) in enumerate(zip(axes.flat, times)):
        frame_panel(ax, play, df, t, lateral, window, rings, throw_t, deco)
        lab = "snap" if abs(t) < 1e-6 else f"{t:+.1f} s"
        ax.set_title(f"{i + 1}.  {lab}", fontsize=8.5, color=style.INK, loc="left")
        if notes:
            ax.text(0.5, -0.03, notes[i], transform=ax.transAxes, ha="center", va="top", fontsize=7.3,
                    color=style.INK, linespacing=1.2)
    fig.tight_layout(h_pad=2.2 if notes else 0.6, w_pad=0.6)
    return fig


def video(play, times, notes=None, lateral=(-24, 4), window=(-8, 14), rings=(), throw_t=None, width_in=5.6,
          deco=None):
    from matplotlib import animation
    from IPython.display import HTML, display
    df = tracking(play)
    span_w, span_d = abs(lateral[1] - lateral[0]), window[1] - window[0]
    fig, ax = plt.subplots(figsize=(width_in, width_in * span_d / span_w + 0.4))
    tmap = df.groupby("frameId")["time"].first()

    def update(fid):
        ax.clear()
        t = float(tmap[fid])
        frame_panel(ax, play, df, t, lateral, window, rings, throw_t, deco)
        txt = "snap" if abs(t) < 0.05 else f"{t:+.1f} s"
        if notes:
            cur = [n for tt, n in zip(times, notes) if tt <= t + 1e-6]
            txt += ("   " + cur[-1].replace("\n", " ")) if cur else ""
        ax.set_title(txt, fontsize=8, color=style.INK, loc="left")
        return []

    anim = animation.FuncAnimation(fig, update, frames=sorted(df["frameId"].unique()), 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 play_movie(play, times, notes=None, **kw):
    """Video on the website; a numbered, captioned frame strip in the PDF."""
    if PDF:
        strip(play, times, notes=notes, **kw)
        plt.show()
    else:
        kw.pop("ncols", None)
        kw.pop("width_in", None)
        video(play, times, notes=notes, **kw)


def set_track(p, key, rows, t_end=None):
    """Hand-made keyframes [(t, d, w), ...] for one player (gridiron's man() ends on top of the
    receiver, and we sometimes need a defender who detours around traffic). Call last."""
    kf, t0, t1 = p._compile()
    kf = dict(kf)
    q = p.players[key]
    rows = [(min(t0, -1.0), q.d, q.w), (0.0, q.d, q.w)] + [tuple(r_) for r_ in rows]
    kf[key] = np.array(rows, dtype=float)
    p._compiled = (kf, t0, max(t1, t_end or rows[-1][0]))


def chase(p, key, target, lag=0.3, off=(-1.0, 0.0), t_start=0.15, t_on=0.8, t_end=4.0, detour=None,
          lag_after=None):
    """Keyframes for a man defender trailing `target`: he starts where he is, then settles `off` =
    (down, toward the offense's right) yards from where the receiver was `lag` seconds earlier.
    detour = (t0, t1, (d, w)): between t0 and t1 he is pulled toward that point (traffic)."""
    q = p.players[key]
    rows = []
    for tt in np.round(np.arange(t_start, t_end, 0.05), 3):
        f = min(1.0, max(0.0, (tt - t_start) / (t_on - t_start)))
        f = f * f * (3 - 2 * f)
        lg = lag
        if detour is not None and lag_after is not None and tt > detour[0]:
            k = min(1.0, (tt - detour[0]) / (detour[1] - detour[0]))
            lg = lag + (lag_after - lag) * k
        tgt = p.position(target, max(0.0, tt - lg)) + np.array(off)
        pos = np.array([q.d, q.w]) * (1 - f) + tgt * f
        if detour is not None:
            a, b, pt = detour
            if a <= tt <= b:
                g = np.sin(np.pi * (tt - a) / (b - a))
                pos = pos * (1 - g) + np.array(pt) * g
        rows.append((tt, *pos))
    return rows


def catch_time(p):
    """Seconds after the snap at which the first pass arrives (rounded up to a tenth)."""
    for kind, to, te in p.ball_events:
        if kind == "pass":
            a, b = p.pass_target_spot()
            return float(np.ceil((te + np.hypot(*(b - a)) / gplay.BALL_SPEED) * 10) / 10)


@contextmanager
def ball_speed(v):
    """Temporarily change gridiron's ball speed (yd/s): a firm deep throw travels faster than the default."""
    old = gplay.BALL_SPEED
    gplay.BALL_SPEED = v
    try:
        yield
    finally:
        gplay.BALL_SPEED = old
Show the Python (nflverse pass attempts by depth and width)
from gridiron.data import nfl, pbp

PCOLS = ["season", "season_type", "game_id", "play_id", "pass_attempt", "sack", "qb_spike", "air_yards",
         "pass_location", "epa", "complete_pass", "success", "interception"]


def attempts(seasons):
    """Regular-season pass attempts (sacks and spikes removed) with a charted depth and direction."""
    d = pbp(seasons, columns=PCOLS)
    return d[(d["season_type"] == "REG") & (d["pass_attempt"] == 1) & (d["sack"] == 0) & (d["qb_spike"] != 1)
             & d["air_yards"].notna() & d["pass_location"].notna()].copy()


ATT = attempts([2023, 2024, 2025])
A25 = attempts([2025])
_p25 = nfl().load_participation([2025]).to_pandas().rename(columns={"nflverse_game_id": "game_id"})
A25 = A25.merge(_p25[["game_id", "play_id", "time_to_throw"]], on=["game_id", "play_id"], how="left")
TTT = A25.groupby(pd.cut(A25["air_yards"], [-99, 0, 10, 20, 99], right=False), observed=True)["time_to_throw"].median()
EPA_BAND = A25.groupby(pd.cut(A25["air_yards"], [-99, 0, 10, 20, 99], right=False), observed=True)["epa"].mean()

Third-and-8, late in the first half. The offense lines up with two receivers on each side. Before the snap the camera shows both safeties standing thirteen yards deep and both cornerbacks only five yards off the ball, flat-footed, watching the quarterback. The ball is snapped, the quarterback takes three quick steps back from the shotgun, and the outside receiver on the right runs five yards and turns around. The cornerback in front of him does exactly what he has been coached to do: he squats on that receiver. The ball sails over his head to the inside receiver, who has run ten yards up the field and angled toward the sideline, into the space behind the cornerback and in front of the safety. First down. On the replay the analyst circles the two routes with his telestrator and says one word: smash. Why was the cornerback wrong when he did what he was told? How did the quarterback know, before the snap, where the ball was going? And why does the same two-route pattern, under one name or another, show up in every playbook in the league? This chapter is about those patterns: the dropback concepts that make up most of an offense’s passing game beyond the quick throws.

You’ll be able to…

NoteYou’ll need

What a pass concept is

A pass concept is a set of routes designed to work together on one play, usually by putting one or two defenders in a position where whatever they do leaves a receiver open. You met the idea in The Quick Game, where slant-flat and stick each pinned one defender between two receivers. This chapter takes the same idea deeper down the field, where the routes take longer to develop and the defenders in conflict are cornerbacks, linebackers and safeties instead of a single slot defender.

Why do offenses think in concepts rather than routes? Three reasons, and they explain a lot about how football is coached.

A concept is a sentence; routes are only words. A corner route on its own means nothing. A corner route run over a hitch, against a cornerback who has to choose between them, is a plan. The quarterback doesn’t read five receivers one by one; he reads one or two defenders and lets the concept tell him where the open man will be. That’s why concepts are drawn and taught as units, and why a receiver learns not only his route but his partner’s.

A name is a shortcut. “Smash” tells two receivers their routes in one syllable, from any formation, on either side of the field. Offenses that name concepts this way can teach a playbook faster and call plays faster, which matters in a two-minute drive or a no-huddle attack. Not every system names its plays like this; some spell out each receiver’s route in a long string of words or numbers, and the trade-offs between those styles are the subject of Offensive Systems and Languages, where concept-based terminology gets its full story.

A concept travels. Smash works from a 2x2 formation, from trips, with a tight end as the inside receiver or a running back. Because the conflict is about space (the gap between a cornerback sitting short and a safety playing deep), the same concept can be dressed up in a dozen formations, which makes it hard for a defense to recognize from the alignment alone.

CautionCommon misconception

“Every pass play has one intended receiver.” Almost none do. A concept has a first look, the route it was designed around, but it’s built so that whichever choice the key defender makes, somebody else is open. When an announcer says the quarterback “went to his second read,” that isn’t a failure of the play. It is often the play working as drawn.

What changes when the quarterback drops back

The quick game lives on throws that leave the quarterback’s hand in about two seconds, aimed at receivers five or six yards away. Dropback concepts give themselves more time and aim farther, and the numbers show what that buys and what it costs. In the 2025 regular season, throws that traveled zero to nine yards past the line of scrimmage left the quarterback’s hand after a median of 2.3 seconds; throws of 10 to 19 yards, the home of most of the concepts in this chapter, took 2.8 seconds, and throws of 20 yards or more took 3.1 seconds.1 That extra half-second is the price of the deeper drop (five steps from under center, or three from the shotgun, in the matched pairs of Passing Fundamentals) and of routes that need ten or fifteen yards to reach the spot where they break.

What does the half-second buy? Value. Those 10-to-19-yard throws averaged about +0.39 expected points added (EPA) per attempt, the course’s measure of how much a play changed the offense’s scoring chances, against about +0.10 for those zero-to-nine-yard throws. But they were completed only 54% of the time, against 72%, and they were intercepted twice as often (about 3.2% of attempts against 1.6%).

ImportantWhy it exists, and what it costs

Dropback concepts trade time and risk for yards. The offense asks its line to protect for three seconds or more, accepts a lower completion rate and more interceptions, and gets in return the throws that move the ball in big pieces: the 15-yard dig on third-and-8, the corner route that turns a drive into a scoring chance. The quick game keeps an offense on schedule; dropback concepts are how it gets ahead of it. The bill comes in sacks, hits on the quarterback and a pass rush with time to get home, which is why protection (Pass Protection) and dropback concepts are planned together.

Four ways to put a defender in a bind

Every concept in this chapter does one of four things to the defense. Learn the four and you can name the purpose of a concept you have never seen, which is most of what “reading” a pass play from the couch means.

Show the Python
fig, axes = plt.subplots(2, 2, figsize=(6.5, 5.0))
# 1 vertical
p1 = Play([O("A", "WR", 0.0, -2.0, "A"), O("B", "WR", 0.0, 2.0, "B")],
          [D("D1", "LB", 6.5, 0.0, "D")], los=LOS, to_go=None)
route_to(p1, "A", (5.0, -2.0), (11.5, -0.6))
route_to(p1, "B", (1.0, 2.0), (2.2, 0.4))
p1.highlight("D1")
# 2 horizontal
p2 = Play([O("A", "WR", 0.0, -7.0, "A"), O("B", "WR", 0.0, 7.0, "B")],
          [D("D1", "LB", 6.0, 0.0, "D")], los=LOS, to_go=None)
route_to(p2, "A", (5.5, -7.0), (6.0, -4.8))
route_to(p2, "B", (5.5, 7.0), (6.0, 4.8))
p2.highlight("D1")
# 3 triangle
p3 = Play([O("A", "WR", 0.0, 7.5, "A"), O("B", "WR", 0.0, 3.0, "B"), O("C", "RB", -2.0, -2.0, "C")],
          [D("D1", "LB", 5.8, 7.4, "D"), D("D2", "LB", 5.6, 3.6, "D")], los=LOS, to_go=None)
route_to(p3, "A", (11.5, 7.5), (10.5, 6.5))
route_to(p3, "B", (1.5, 5.0), (2.5, 10.5))
route_to(p3, "C", (-1.0, -0.4), (3.5, 1.0))
p3.highlight("D1", "D2")
# 4 man-beater
p4 = Play([O("A", "WR", 0.0, -6.0, "A"), O("B", "WR", 0.0, 6.0, "B")],
          [D("D1", "CB", 1.4, -5.6, "D"), D("D2", "CB", 1.4, 5.6, "D")], los=LOS, to_go=None)
route_to(p4, "A", (5.0, -5.6), (6.5, 6.5))
route_to(p4, "B", (4.0, 5.6), (5.0, -6.5))
for ax, (p, ttl) in zip(axes.flat, ((p1, "1. Vertical stretch (high-low)"), (p2, "2. Horizontal stretch"),
                                    (p3, "3. Triangle"), (p4, "4. Man-beater"))):
    fld = draw(p, ax=ax, lateral=(-11.5, 11.5), window=(-3.5, 14.5), show_ruler=False, width_in=3.2)
    panel_title(ax, ttl, fs=8.5)
    if p is p1:
        opening(fld, p, (11.0, -0.6), (2.2, 3.2))
        opening(fld, p, (2.3, 0.4), (2.0, 3.2))
        say(fld, p, 11.6, 5.4, "high", color=OD)
        say(fld, p, 2.3, 4.6, "low", color=OD)
        say(fld, p, 6.5, -5.4, "one defender,\ntwo depths", color=DD)
    elif p is p2:
        say(fld, p, 9.0, 0.0, "one defender,\ntwo sides", color=DD)
        say(fld, p, 2.4, -4.5, "inside", color=OD)
        say(fld, p, 2.4, 4.5, "outside", color=OD)
    elif p is p3:
        triangle(fld, p, [(10.5, 6.5), (2.5, 10.5), (3.5, 1.0)])
        say(fld, p, 12.4, -3.0, "two defenders,\nthree receivers", color=DD)
    else:
        branch(fld, p, [(1.4, -5.6), (4.0, -4.6), (4.4, -1.6), (2.6, 0.2), (4.0, 2.6), (5.2, 4.4)],
               color=style.DEFENSE, lw=1.4, dash=(0, (1, 1.6)))
        say(fld, p, 11.0, 0.0, "B's path is in the way:\nA's defender has to\ngo around (dotted)", color=DD)
fig.tight_layout(h_pad=1.2, w_pad=1.0)
plt.show()
Figure 23.1: The four families of pass concepts, drawn as small abstract pictures (offense moving up the page). 1. Vertical stretch (high-low): two receivers in one defender’s area at two depths; he can stand under the high one or in front of the low one, not both. 2. Horizontal stretch: receivers on both sides of a defender at about the same depth. 3. Triangle: three receivers around two defenders, one high (A’s curl) and two low (B’s flat outside, C’s hook inside), so the quarterback has a high-low and an inside-outside choice at once. 4. Man-beater: against man coverage there is no area to overload, so the routes cross close to each other and the defenders chasing them have to fight through traffic (the dotted orange line is a defender forced to go around).
  1. Vertical stretch (high-low). Two receivers in one defender’s area, one above him and one below. He can sink under the high route or come up on the low one, not both. This is the high-low read of Passing Fundamentals, and it is the most common family by far: smash, flood, levels, drive, shallow cross, dagger, Y-cross and yankee are all built on it. Notice that several of these (drive, shallow cross, Y-cross) use crossing routes: what makes them vertical stretches is that, at the moment of the throw, one route is above the key defender and one below him.
  2. Horizontal stretch. Receivers on both sides of a defender at about the same depth, the horizontal stretch of Passing Fundamentals. Underneath, that’s the quick game’s spacing; deep, it is four verticals, post-wheel and scissors, which stretch the deep defenders side to side: two routes split one deep defender’s area (or two defenders’ leverage) inside and outside.
  3. Triangle. Three receivers arranged around two defenders. A triangle read is a three-receiver read in which the routes form a triangle, one high and two low (or two high and one low), so the quarterback has a high-low and an inside-outside choice at once. Snag, from the quick game, is the classic; curl-flat with a third route inside is the dropback version.
  4. Man-beater. A man-beater is a concept designed to beat man coverage. Stretches beat zone defenders, who guard areas; a man defender follows one receiver wherever he goes, so there is no area to overload. Man-beaters win instead with routes that cross close together (so defenders run into traffic), routes that break away from a defender’s leverage, and mismatches like a running back against a linebacker. Mesh and the angle route are the classics. Crossing alone doesn’t make a man-beater; what makes one is that it wins by making a man defender run through traffic or lose his leverage, not by stretching a zone.

Most real concepts are hybrids (a flood is a vertical stretch that also stretches the flat defender sideways; mesh sits in zone holes when it isn’t running away from man defenders), so the family names describe a concept’s main idea. That idea also tells you what coverage the concept is built to beat, and therefore when an offense will call it. We’ll come back to that matchup at the end of the chapter, and What Beats What builds it into a full table.

How to read the plates

The fourteen concepts below are drawn as plates, the way a playbook draws them, in one consistent style:

  • The offense is in shotgun with 11 personnel (one back, one tight end, three receivers) unless the caption says otherwise: X and Z are the outside receivers, H a slot receiver, Y a tight end or second slot, R the running back.
  • Blue routes are the concept. Grey routes are part of the call but outside the read: they are there to hold defenders in place. They aren’t forbidden; if a grey route is plainly open (a backside receiver left alone, say), a quarterback may take it, and many teams let him peek at a one-on-one on the backside before the snap. But he doesn’t read his way to them.
  • Green numbers are the progression, the order in which the quarterback looks. These are typical teaching orders; every team writes its own, and many change the order by coverage.
  • The defense is drawn in the coverage the concept is built to beat. Dashed orange arrows are zone drops; the ringed defender is the one in conflict, the man the quarterback reads. In the man-coverage plates, dotted lines join each defender to his man; which linebacker takes the back varies by team and call, and here it is always the Will, with the Mike free to rush or to sit in the middle.
  • The thick orange line is the line to gain; most plates assume ten yards to go, and the caption says so when it doesn’t.
  • The label at the top right names the concept’s family.

Two more things the plates simplify. First, receivers run to landmarks, spots on the field rather than bare depths, and you can see them on a broadcast: seam routes about two yards outside the hash marks (wider against one deep safety), go routes on or outside the painted numbers so there are five or six yards left for a back-shoulder throw, the smash corner route toward a spot about six yards from the sideline, and the Y-cross to the far numbers (Coverage Fundamentals teaches the defense’s landmarks). Second, the ball is drawn in the middle of the field to keep the picture symmetric. In a game it usually sits on a hash, and concepts get aimed: flood, Y-cross and post-wheel usually go toward the wide side, where the routes have room to finish; smash is often run into the short side against Cover 2, because the corner route is a shorter throw there.

High to low, or low to high? Each plate’s progression follows one of two rhythms, and knowing which saves you memorizing fourteen orders. Most vertical stretches are read from the top down: look at the deep route first, because if it’s open it’s the bigger gain, then come down to the short one, which will still be there a beat later. Some are read from the bottom up: a shallow crosser against man coverage wins immediately or not at all, so it gets the first look. And on a money down the progression often starts with whichever route reaches the line to gain. Each plate says which rhythm it uses.

On the sideline: smash, flood and the curl-flat triangle

The first three concepts attack the area near one sideline. Two of them (smash and flood) are vertical stretches; the third (curl-flat) is the dropback game’s triangle. Coaches like to say the sideline is an extra defender, for the defense, because a receiver can’t run past it and a throw that drifts lands out of bounds. Offenses attack it anyway because of arithmetic: out there a defense has only two or three players guarding a wide strip of grass, against five or more crowded between the hashes.

Smash: the corner over the hitch

Smash pairs two receivers on one side. The outside receiver runs a hitch at five or six yards and turns back to the quarterback. The inside receiver runs a corner route: ten or twelve yards straight up the field, then an angle toward the sideline and the back of the end zone. The defender in conflict is the cornerback on that side, and the read is high to low: the corner route first.

Show the Python
p = Play(spread(), cover2(extra={"RCB": (5.0, 17.2), "LCB": (5.0, -17.2)}), los=LOS, to_go=8)
protect(p)
p.dropback(2.5)
p.route("Z", r.hitch(6.0))
route_to(p, "Y", (10.5, 9.0), (18.0, 20.0))
rb_n = swing(p, -1)
p.drop("RCB", (6.5, 18.6))
p.drop("SS", (18.5, 11.5))
p.drop("NB", (8.5, 8.4))
p.drop("MIKE", (9.0, 1.2))
p.drop("WILL", (8.5, -8.0))
p.drop("FS", (18.5, -11.5))
p.drop("LCB", (6.5, -18.6))
p.highlight("RCB")
fld = plate(p, nums=[(1, (19.6, 22.0)), (2, (7.0, 13.2)), (3, rb_n)],
            ghosts=[[(-0.7, -15.0), (6.3, -15.0), (5.3, -14.4)], [(-1.5, -9.0), (10.5, -9.0), (18.0, -20.0)]],
            stretch="Vertical stretch: the cornerback")
opening(fld, p, (15.5, 18.0), (4.6, 5.6))
point_to(fld, p, (22.0, 3.0), (16.0, 16.0), "corner route lands\nbehind the CB, in front\nof the safety",
         color=READ_GREEN)
say(fld, p, 2.4, 21.8, "CB squats\non the hitch", color=DD)
plt.show()
Figure 23.2: Smash to the offense’s right against Cover 2, third-and-8. Z runs a hitch at about six yards; Y, from the slot, breaks at about ten and angles toward a spot six yards from the sideline, about 18 yards deep. The ringed cornerback squats in the flat, as Cover 2 tells him to; the strong safety (SS) has the deep half behind him but starts thirteen yards deep and well inside: the corner route lands in the gap between them (green). Progression, high to low: (1) the corner route if the cornerback sits on the hitch, (2) the hitch if he sinks, (3) the back as the checkdown. The left side (grey) is a mirror-image smash, so the quarterback can pick a side before the snap.

Why does it work against Cover 2? Cover 2 splits the deep field between two safeties and asks the cornerbacks to stay short, guarding the flats, so that nothing quick gets thrown to the outside. That leaves a seam of open grass along each sideline between the corner’s short zone and the safety’s deep half, roughly 12 to 20 yards deep, and the safety has to cover it from his spot near the hash, ten or fifteen yards away. The corner route aims at that seam. Whatever the cornerback does, one of the two routes is on the wrong side of him, so the quarterback only has to watch one man.

The corner route has to be thrown with care. Too flat and the cornerback can recover under it; too high and the safety arrives. Many teams build a rule into the route (a route conversion, which we’ll get to). Against two deep safeties, whose cornerbacks squat, the receiver takes it to depth, 15 to 20 yards along the sideline, over the cornerback and away from the safety. Against one deep safety, whose cornerback bails into a deep third, a corner route to that depth runs straight into him, so the receiver flattens it at about 12 to 14 yards, underneath the bailing cornerback, and some teams convert it to a post.

On third-and-8 there is one more wrinkle a coach would point out: a six-yard hitch is short of the sticks. It needs yards after the catch, which is one more reason the quarterback wants the corner route, and why some teams push the hitch to the line to gain on long downs.

What smash gives up. Against Cover 3, whose cornerbacks drop into deep thirds instead of squatting, a corner route run to full depth runs into a defender standing right on top of it, so the read flips: the cornerback bails, and the hitch underneath (or the flattened corner route) is the throw. Against man coverage, smash becomes two one-on-one routes, a hitch that has to beat a cornerback and a corner route that has to beat a slot defender, which is fine if the receivers are better than the defenders and not otherwise. Smash is a Cover 2 beater first, a decent answer to Cover 3 second, and only a hope against man.

Flood: three levels on one sideline

Flood (many teams call it sail) puts three receivers at three depths near one sideline: one deep, one in between and one shallow. On the strong side of a Cover 3 defense there are only two defenders in that area, the cornerback, who has the deep third, and the curl-flat defender underneath. Three receivers, two defenders.

Show the Python
p = Play(spread(rb_w=1.5), cover3(nb_side=1), los=LOS, to_go=10)
protect(p)
p.dropback(2.5)
p.route("Z", r.go(23.0))
route_to(p, "Y", (8.5, 9.0), (13.0, 17.8))
route_to(p, "RB", (-3.6, 5.0), (1.0, 11.5), (2.4, 16.5))
p.drop("RCB", (17.0, 16.6))
p.drop("NB", (8.0, 12.5))
p.drop("MIKE", (8.0, 3.0))
p.drop("FS", (17.0, 0.0))
p.drop("WILL", (8.0, -4.0))
p.drop("SS", (7.0, -12.0))
p.drop("LCB", (17.0, -16.6))
p.highlight("NB")
fld = plate(p, nums=[(1, (13.0, 20.3)), (2, (2.4, 19.2))],
            ghosts=[[(-0.7, -15.0), (11.3, -15.0), (10.0, -13.8)], [(-1.5, -9.0), (20.0, -7.6)]],
            stretch="Vertical stretch: three levels")
say(fld, p, 19.5, 10.5, "go: clears\nthe corner", color=OD)
say(fld, p, 9.6, 21.7, "sail", color=OD)
say(fld, p, -1.4, 21.3, "flat", color=OD)
plt.show()
Figure 23.3: Flood to the offense’s right against Cover 3. Z’s go route carries the cornerback deep (the clear-out). Y runs a sail route: ten yards up the field, then an angle toward the sideline that settles at about 13 yards. The back runs to the flat. The ringed nickel ($), the curl-flat defender, is the man in conflict. Read high to low: (1) the sail, (2) the flat. The go route is a bonus only if the cornerback lets Z run past him. On the left, X and H (grey) keep the Will and the free safety from drifting over.

Flood is a vertical stretch with three floors instead of two. The go route occupies the top floor and takes the cornerback with it, because a Cover 3 cornerback is taught never to let anyone get behind him. That leaves the curl-flat defender alone with the sail and the flat: the same high-low as the curl-flat read of Passing Fundamentals and slant-flat in the quick game, but with the high route farther outside and deeper, where he has to run farther to get under it. If he comes up for the flat, the sail settles in the space he left, in front of the cornerback, for 12 or 14 yards. If he sinks, the back catches the ball in the flat with open grass in front of him.

Flood’s cost is the other side of the field. It sends three receivers to one sideline, so the backside routes are usually just there to hold defenders, and a defense that rotates its coverage toward the flood (a safety rolling down to the flat side at the snap) can outnumber it. That is why flood is so often run off a fake run or with the quarterback rolling toward the flood side; the run fake and the bootleg, which move the defenders before the routes even arrive, are in Play-Action, Bootlegs, and Screens.

Curl-flat: the West Coast triangle

Curl-flat is the high-low you met in Passing Fundamentals: a curl at 10 to 12 yards over a flat route underneath, both in the area of the one defender who guards the short outside zone. As a dropback concept it usually comes with a third route, a back or tight end who settles in a hook route just inside the curl at five or six yards. That third route turns a two-man read into a triangle.

Show the Python
p = Play(spread(rb_w=1.5), cover3(nb_side=1, extra={"MIKE": (5.0, 3.6)}), los=LOS, to_go=10)
protect(p)
p.dropback(2.5)
p.route("Z", r.curl(12.0))
route_to(p, "Y", (1.0, 11.5), (2.0, 17.5))
route_to(p, "RB", (-3.5, 1.0), (1.0, 0.8), (4.6, 0.6), delay=0.4)
p.drop("NB", (8.5, 11.0))
p.drop("MIKE", (7.6, 5.6))
p.drop("RCB", (17.0, 16.6))
p.drop("FS", (17.0, 0.0))
p.drop("WILL", (8.0, -4.0))
p.drop("SS", (7.0, -12.0))
p.drop("LCB", (17.0, -16.6))
p.highlight("NB", "MIKE")
fld = plate(p, nums=[(1, (12.8, 12.6)), (2, (4.4, 19.4)), (3, (6.8, -0.2))],
            ghosts=[[(-0.7, -15.0), (11.3, -15.0), (9.8, -13.8)], [(-1.5, -9.0), (20.0, -7.6)]],
            stretch="Triangle: the $ and the Mike")
triangle(fld, p, [(9.8, 13.8), (2.0, 17.5), (4.6, 0.6)])
plt.show()
Figure 23.4: Curl-flat to the offense’s right, with the back’s hook route inside, against Cover 3. Z runs a curl at 12 yards, Y runs to the flat, and the back checks for a blitzer, then settles at five yards over the ball, just inside the Mike. The three routes form a triangle (shaded) with two defenders inside it: the ringed nickel ($), who guards the curl-flat area, and the ringed Mike (M), who guards the hook. Read high to low, then inside: (1) the curl, if the $ widens to the flat; (2) the flat, if he sinks under the curl; (3) the hook, if the Mike widens to help on the curl. The left side runs a mirror-image curl and a seam (grey).

The triangle is what makes the concept durable. The nickel can’t cover the curl and the flat (that’s the high-low), and if he splits the difference, the Mike has to choose between widening to help on the curl and staying home on the hook (that’s the in-out). The quarterback reads it with the same rhythm on almost every snap: eyes to the curl as his back foot lands, then down to the flat, then inside to the hook. The routes are short enough that the ball comes out in under three seconds, which is why curl-flat sits at the border between the quick game and the dropback game.

Curl-flat is a West Coast staple, the family of offenses descended from Bill Walsh’s 49ers, which were built on high-percentage throws with a third receiver underneath as the safety valve. Its weakness is its depth. Every route in the triangle is within 12 yards of the line of scrimmage, so a defense that squeezes the short zones and tackles well can hold it to a five-yard gain; and against two-high coverages, whose cornerbacks often sit short and outside, the curl can find a defender standing right on top of it.

Vertical stretches in the middle: levels, drive, shallow cross and dagger

The next four concepts carry the high-low into the middle of the field, where the linebackers live. The middle is harder to throw into (there are more defenders there, and they are closer together) but it’s also the most valuable area on the field, as the chart at the end of this section shows. The defender in conflict in each of these concepts is a linebacker, or a safety standing behind the linebackers, and the routes are mostly in-breakers: digs, shallow crosses and in routes that run across his face.

Levels: two in-breakers, two depths

Levels sends two receivers from the same side across the field on in-breaking routes at two depths: in the most common version the outside receiver runs a short in at about five yards and the inside receiver a dig at about twelve. The defender between those levels is the linebacker guarding the hook area on that side.

Show the Python
p = Play(spread(rb_w=1.5), cover3(nb_side=-1), los=LOS, to_go=10)
protect(p)
p.dropback(2.5)
p.route("H", r.dig(13.0, 12.0))
route_to(p, "X", (3.2, -15.0), (3.6, -5.0))
rb_n = swing(p, 1)
p.drop("WILL", (7.5, -6.5))
p.drop("NB", (7.0, -12.0))
p.drop("MIKE", (7.5, 3.5))
p.drop("SS", (7.0, 11.5))
p.drop("LCB", (17.0, -16.6))
p.drop("RCB", (17.0, 16.6))
p.drop("FS", (17.0, 0.0))
p.highlight("WILL")
fld = plate(p, nums=[(1, (12.4, 5.6)), (2, (1.4, -12.2)), (3, rb_n)],
            ghosts=[[(-1.5, 9.0), (20.0, 7.6)], [(-0.7, 15.0), (21.0, 15.0)]],
            stretch="Vertical stretch: the hook linebacker")
say(fld, p, 14.6, -9.0, "dig (high)", color=OD)
say(fld, p, 2.0, -20.0, "in (low)", color=OD)
plt.show()
Figure 23.5: Levels to the offense’s left against Cover 3. H, from the slot, runs a dig at 12 yards; X, from outside, runs a short in route at about 4 yards underneath him. The ringed Will linebacker (W), who drops to the hook area on that side, is the man in conflict: (1) the dig if he jumps the short in route; (2) the in route if he drops under the dig; (3) the back, who checks for a blitzer, then swings out to the right. On the right, Y’s seam and Z’s go route (grey) keep the free safety in the middle and the cornerback deep.

Why in-breakers? Because the linebacker in the middle is usually watching the quarterback, not the receivers, and he drops backward, which makes him slow to react to a route crossing in front of him or behind him. The short in route arrives first; the dig arrives a beat later, behind him. Coaches teach the quarterback to look high first: if the linebacker comes up on the in route, the dig behind him is the bigger gain. Peyton Manning’s Indianapolis offenses (where the play was called “Dig”) ran levels so often, by Chris Brown’s count, that ten times a game was not unusual, and its logic, two in-breakers on two floors, runs through many of the middle-of-the-field concepts that follow.2

Drive: the shallow and the dig, together

The next two concepts are built on one route, the shallow: a receiver crossing the whole field just past the line of scrimmage, two to five yards deep (some teams call it a drag). Each concept pairs it with a deeper in-breaker; what differs is where the two come from.

Drive is levels stretched across the whole field. One receiver runs a shallow, two to four yards deep, all the way to the far side; a teammate from the same side runs a dig at about twelve yards behind him, following him across. The pair passes through the hook zones of both linebackers, but the quarterback reads the first one they reach, the Mike: the ball comes out as the shallow clears him, long before the routes get to the Will. Unlike levels, drive is usually read low to high, because the shallow is open early or not at all.

Show the Python
p = Play(spread(), cover3(nb_side=1), los=LOS, to_go=10)
protect(p)
p.dropback(2.5)
route_to(p, "Y", (2.0, 8.2), (2.7, 4.5), (2.7, -13.0))
p.route("Z", r.dig(12.0, 24.0))
rb_n = swing(p, -1)
p.drop("MIKE", (7.0, 2.0))
p.drop("WILL", (7.0, -4.5))
p.drop("NB", (7.0, 12.0))
p.drop("SS", (7.0, -12.0))
p.drop("LCB", (17.0, -16.6))
p.drop("RCB", (17.0, 16.6))
p.drop("FS", (17.0, 0.0))
p.highlight("MIKE")
fld = plate(p, nums=[(1, (4.0, -4.7)), (2, (13.6, -4.4)), (3, rb_n)],
            ghosts=[[(-0.7, -15.0), (21.0, -15.0)], [(-1.5, -9.0), (20.0, -7.6)]],
            stretch="Vertical stretch: the Mike")
say(fld, p, 1.4, 21.0, "shallow at 2-4 yd", color=OD)
say(fld, p, 14.0, 20.0, "dig at 12 yd", color=OD)
plt.show()
Figure 23.6: Drive against Cover 3. From the right, Y runs a shallow at about three yards across the formation; Z runs a 12-yard dig behind him on the same path. The ringed Mike (M) can step up to the shallow or sink under the dig, not both. Read low to high, as each route crosses the Mike’s area: (1) the shallow, if he drops; (2) the dig, if he comes up; (3) the back, who checks for a blitzer and swings left. X’s go route and H’s seam (grey) take the left cornerback and the free safety deep, clearing the area where both crossers finish.

The shallow is the engine. A receiver crossing the field at three yards is a nightmare in man coverage, because his defender has to chase him through the traffic of linemen and linebackers and can never get in front of him; against zone, he runs through one hook defender after another, and each one has to decide whether to come up and take him. The moment one does, the dig is open behind. That makes drive one of the most useful concepts in football: a man-beater and a zone-beater in the same call. Drive is in a great many NFL playbooks, often under that name, and it’s a favorite of offenses that like to make the defense tackle a receiver who catches the ball running full speed across the field.

Shallow cross: the Air Raid’s bread and butter

Shallow cross uses the same two routes as drive, a shallow under a deeper in-breaker, but sends them from opposite sides. One receiver runs the shallow at three to five yards; the receiver from the other side runs a dig or a “sit” route at 10 to 12 yards, settling over the ball. (A sit route runs up to a depth and simply stops in the open space between zone defenders, facing the quarterback; against man it keeps running, as a dig.) Like drive, it is read low to high. The two routes cross the middle of the field going in opposite directions, so the middle linebacker has a receiver coming from each side at once.

Show the Python
p = Play(spread(), cover2(), los=LOS, to_go=10)
protect(p)
p.dropback(2.5)
route_to(p, "Z", (2.0, 13.0), (2.8, 9.0), (2.8, -12.0))
route_to(p, "H", (8.0, -9.0), (11.0, -6.5), (11.0, 2.0))
rb_n = swing(p, -1)
p.drop("MIKE", (9.5, 0.0))
p.drop("WILL", (8.5, -8.0))
p.drop("NB", (8.5, 8.4))
p.drop("LCB", (6.0, -17.2))
p.drop("RCB", (6.0, 17.2))
p.drop("FS", (18.5, -11.5))
p.drop("SS", (18.5, 11.5))
p.highlight("MIKE")
fld = plate(p, nums=[(1, (4.6, -11.2)), (2, (12.6, 3.6)), (3, rb_n)],
            ghosts=[[(-0.7, -15.0), (21.0, -15.0)], [(-1.5, 9.0), (21.0, 7.5)]],
            stretch="Vertical stretch: the Mike")
say(fld, p, 1.0, 19.5, "shallow at 3 yd", color=OD)
say(fld, p, 13.0, -14.2, "dig or sit at 11 yd", color=OD)
plt.show()
Figure 23.7: Shallow cross against Cover 2. From the right, Z runs a shallow at about three yards to the left; from the left, H runs a dig that settles at 11 yards over the ball. The ringed Mike (M), dropping into the middle, is the read: (1) the shallow, if he sinks; (2) the dig, if he comes up for the shallow; (3) the back on the left as the checkdown. X and Y (grey) run vertically to hold the two safeties, so nobody sinks under the dig from above. Against man coverage, the shallow simply runs away from his defender, and the two crossing paths add traffic.

The shallow cross is the play most associated with the Air Raid, the pass-first college offense built by Hal Mumme and Mike Leach (its story is below), which ran it with wide line splits and a quarterback taught to throw the shallow the instant the linebacker dropped. It works for the same reason drive does, plus one more: because the two crossers come from opposite sides, a man defender chasing the shallow may have to run around the dig receiver coming the other way. That is the first appearance of an idea we’ll make explicit with mesh, the rub.

Dagger: clear the seam, fill it behind

Dagger is the deep member of the family, and the one built to beat two-high coverages. The inside receiver runs straight up the seam; the outside receiver runs a deep dig, 15 to 18 yards, coming in behind him.

Show the Python
p = Play(spread(), quarters(extra={"SS": (10.0, 5.6), "FS": (10.0, -5.6)}), los=LOS, to_go=10)
protect(p)
p.dropback(3.0)
p.route("Y", r.seam(24.0))
p.route("Z", r.dig(15.5, 19.0))
rb_n = swing(p, -1)
p.drop("SS", (19.0, 9.0))
p.drop("NB", (11.0, 8.6))
p.drop("MIKE", (9.0, 3.0))
p.drop("WILL", (8.5, -7.0))
p.drop("FS", (13.0, -11.0))
p.drop("LCB", (16.0, -16.6))
p.highlight("SS")
fld = plate(p, nums=[(1, (21.0, 10.6)), (2, (13.2, -7.0)), (3, rb_n)],
            ghosts=[[(-0.7, -15.0), (11.3, -15.0), (9.8, -13.8)], [(-1.5, -9.0), (5.0, -9.0), (5.2, -7.0)]],
            stretch="Vertical stretch: safety and linebackers",
            keys=["route", "ghost", "drop", "react", "num", "ring", "ltg"])
branch(fld, p, [(7.5, 16.0), (11.5, 16.4), (15.0, 13.0)], color=style.DEFENSE, lw=1.4)
opening(fld, p, (14.8, -1.0), (4.0, 9.0))
say(fld, p, 22.6, 4.0, "the seam pulls\nthem deep", color=OD)
say(fld, p, 18.6, -1.5, "dig fills in\nbehind them", color=READ_GREEN, weight="bold")
plt.show()
Figure 23.8: Dagger to the offense’s right against Quarters. Y runs straight up the seam; Z runs a dig at about 15 yards, in behind him. The ringed strong safety (SS) reads Y, the inside receiver, so the seam pulls him deep. The nickel runs with the seam for a few steps and the Mike drops to his hook zone, both short of the dig; the cornerback, playing Z from outside, can only trail a route that breaks inside (dashed). The dig settles under the safeties and over the linebackers (green). Read high to low: (1) the seam, if the safety doesn’t carry it; (2) the dig; (3) the back, who checks for a blitzer, then swings left. On the left, X and H (grey) keep the free safety from drifting over.

Why would two-high coverages be vulnerable to this? In Quarters and its relatives, each safety reads the inside receiver on his side: if that receiver goes vertical, the safety takes him deep; if he runs short, the safety is free to help on the outside receiver, usually by jumping an in-cut. That rule is what makes Quarters so good against the run and the quick game, because the safety can come up hard on anything short. The cornerback, meanwhile, plays the outside receiver almost man-to-man, from outside leverage.

Dagger turns both rules against them. The seam takes the safety, the one defender whose job was to rob the dig, out of the picture. The underneath defenders don’t come up on a vertical route, as you might expect; they are taught to run with it for a few steps, because a seam thrown over their heads is the play that beats them. So the nickel carries the seam and the Mike drops to his hook zone, eight or nine yards deep. That leaves the cornerback alone on the dig, and he is on the wrong side of it: playing from outside, he can only trail a receiver who breaks inside. The dig, timed to arrive a beat after the seam, settles in the empty middle between the levels.

Against one-high coverage the read is similar but the threat is different: the seam attacks the single safety, and if he stays in the middle, the dig crosses under him. Dagger needs time, because a 15-yard dig takes a deep drop and three seconds or more, which is why it’s usually called with six-man protection, or with five and the back checking for a blitzer before he releases, as drawn.

Where the value is

Why do offenses work so hard to throw into the middle, where the linebackers are, rather than to the open sideline? The chart answers that with three seasons of throws.

Show the Python
order = ["30+", "20–29", "15–19", "10–14", "5–9", "0–4", "behind"]
bins = [-99, 0, 5, 10, 15, 20, 30, 99]
labs = ["behind", "0–4", "5–9", "10–14", "15–19", "20–29", "30+"]
h = ATT.copy()
h["depth"] = pd.cut(h["air_yards"], bins, labels=labs, right=False)
g = h.groupby(["depth", "pass_location"], observed=True)["epa"].agg(["mean", "size"]).reset_index()
M = g.pivot(index="depth", columns="pass_location", values="mean").loc[order, ["left", "middle", "right"]]
N = g.pivot(index="depth", columns="pass_location", values="size").loc[order, ["left", "middle", "right"]]
from matplotlib.colors import LinearSegmentedColormap, TwoSlopeNorm
cmap = LinearSegmentedColormap.from_list("div", [style.DIVERGING[2], style.DIVERGING[1], style.DIVERGING[0]])
fig, ax = plt.subplots(figsize=(5.4, 4.0))
ax.imshow(M.values, cmap=cmap, norm=TwoSlopeNorm(vmin=-0.6, vcenter=0.0, vmax=0.6), aspect="auto")
for i in range(M.shape[0]):
    for j in range(M.shape[1]):
        v = M.values[i, j]
        ax.text(j, i - 0.08, f"{v:+.2f}", ha="center", va="center", fontsize=9.5,
                color="white" if abs(v) > 0.42 else style.INK, fontweight="bold")
        ax.text(j, i + 0.26, f"n = {int(N.values[i, j]):,}", ha="center", va="center", fontsize=6.6,
                color="white" if abs(v) > 0.42 else style.INK_2)
ax.set_xticks(range(3), ["left third", "middle", "right third"], fontsize=8.5)
ax.set_yticks(range(len(order)), [f"{o} yd" if o != "behind" else "behind LOS" for o in order], fontsize=8.5)
ax.set_xlabel("Where the ball went (offense's view)", fontsize=8.5)
ax.set_ylabel("Air yards", fontsize=8.5)
ax.grid(False)
for sp in ax.spines.values():
    sp.set_visible(False)
ax.add_patch(Rectangle((0.5, 1.5), 1.0, 2.0, fill=False, ec=style.INK, lw=1.6, zorder=5))
ax.set_title("EPA per attempt by depth and direction, 2023–2025", fontsize=9.5, loc="left")
fig.tight_layout()
plt.show()
Figure 23.9: Expected points added (EPA) per pass attempt by where the ball was thrown, 2023–2025 regular seasons: rows are depth past the line of scrimmage (air yards), columns are the play-by-play charter’s left, middle and right thirds of the field. Blue cells added value on average, red cells lost it; the small number is the count of attempts. The intermediate middle (10–20 yards between the hashes, where the digs of levels, drive, shallow cross and dagger are thrown) is, with the deep middle, the most valuable area on the field per attempt, worth half a point or more. Deep throws are valuable everywhere but rarer; throws behind the line lose value on average. Sacks and spikes are excluded; interceptions count against the cell they were thrown to.

The boxed cells are the payoff. Over three seasons, a throw of 10 to 19 yards into the middle of the field added about half an expected point on average, more than the same depth toward either sideline and more than any short throw anywhere.3 Two cautions keep the chart honest. First, it shows where the ball went, not where the quarterback wanted it to go: quarterbacks throw into the middle when somebody is open there, so the cells partly measure good decisions. Second, the middle is also where interceptions happen (the defenders are closer together), and those are already folded into the averages. (The worst cell, throws behind the line into the middle, is a small and messy one: about a third are screens, one in nine is a throwaway, and only 60% are completed, against about 80% for throws behind the line toward either sideline; the middle behind the line is crowded with linemen and pass rushers.) Even so, the shape explains a great deal of offensive design: the intermediate middle is worth fighting for, and the concepts in this section are the tools for getting there.

Y-cross: a flood built from across the field

Y-cross combines the two ideas so far, the three-level flood and the in-breaker through the middle. A receiver from one side (in the Air Raid’s lettering, the Y, the inside receiver) runs a crossing route that climbs to 15 to 18 yards as it goes and finishes at the far numbers. On that far side, the outside receiver runs a go route to clear the cornerback away and another receiver runs to the flat. The result is a flood whose middle level arrives from the other side of the field, at full speed, where no defender on that side was looking for it.

Show the Python
p = Play(spread(rb_w=1.5), cover3(nb_side=1, extra={"FS": (14.5, 0.0)}), los=LOS, to_go=10)
protect(p)
p.dropback(3.0)
route_to(p, "Y", (6.5, 9.2), (10.0, 3.0), (15.5, -12.0))
p.route("X", r.go(23.0))
route_to(p, "H", (0.5, -11.5), (2.0, -17.0))
rb_n = swing(p, 1)
p.drop("SS", (8.0, -12.0))
p.drop("LCB", (17.0, -16.6))
p.drop("FS", (18.0, 0.0))
p.drop("WILL", (8.0, -4.0))
p.drop("MIKE", (8.0, 3.0))
p.drop("NB", (8.0, 12.0))
p.drop("RCB", (17.0, 16.6))
p.highlight("SS")
fld = plate(p, nums=[(1, (17.5, -10.4)), (2, (3.6, -19.6)), (3, rb_n)],
            ghosts=[[(-0.7, 15.0), (11.3, 15.0), (21.0, 8.0)]],
            stretch="Vertical stretch: three levels")
say(fld, p, 23.8, -11.6, "go: clears\nthe corner", color=OD)
say(fld, p, 20.4, -4.0, "the cross climbs\nto about 16", color=OD)
plt.show()
Figure 23.10: Y-cross against Cover 3. Y, from the right slot, pushes up past the nickel, then crosses the field under the free safety, climbing to about 16 yards toward the far numbers. On the left, X’s go route takes the cornerback deep and H runs to the flat underneath. Z’s post (grey) holds the free safety in the middle. The ringed strong safety, the curl-flat defender on the left, faces a high-low, read high to low: (1) the cross if he comes up to the flat; (2) the flat if he sinks under the cross; (3) the back, who checks for a blitzer, then swings right. Teams usually run it toward the wide side of the field, so the crosser has room to finish.

The quarterback reads Y-cross like a flood: eyes on the curl-flat defender on the far side, cross over flat. What makes it special is timing and angle. The crosser is running away from the defenders who started on his side and toward a defender who has to turn and find him, so he arrives in the window with momentum and with no one trailing who can catch up. That also makes it a good answer to man coverage, since a crossing route is the hardest route for a man defender to stay on top of. The price is time: a crosser at 15 to 18 yards needs three seconds or more, so Y-cross asks a lot of the pass protection.

NoteGo deeper: the Air Raid’s small menu

The Air Raid was built at small colleges, not in the NFL. Hal Mumme, a former high school coach and college assistant, and Mike Leach, a law-school graduate who had never played college football, put it together at Iowa Wesleyan around 1989, took it to Valdosta State and then, in 1997, to the University of Kentucky, where quarterback Tim Couch became the first pick of the 1999 NFL draft. Leach went on to run it as a head coach at Texas Tech, Washington State and Mississippi State.4

Their starting point was the passing game of LaVell Edwards’s BYU teams, which they studied in person, and their central idea was subtraction: run a handful of concepts (mesh, shallow cross, Y-cross, four verticals, a few quick-game calls) from a handful of formations, and practice them so many times that the quarterback and receivers react instead of think. Where an NFL playbook might carry hundreds of pass plays, the Air Raid lived on a dozen or so. Its lesson for this chapter is that a concept’s power comes as much from repetition as from design: a quarterback who has read the same curl-flat defender on the same Y-cross ten thousand times throws the ball before the defender has finished deciding. The full story of the Air Raid’s spread through college and then the NFL is told in The College Laboratory.

Man-beaters: mesh, the rub and the angle route

Everything so far is built to beat zone defenders, who guard areas and can be put between two receivers. Man coverage changes the problem. Each defender has one receiver and follows him, so there is no area to overload and no defender in two minds; the defense is betting its players can stay with yours. The most common version in the NFL is Cover 1: a man on every receiver, one safety deep in the middle, and often one more defender free to rush or to sit in the middle as a helper (Man Coverage builds it from the defense’s side). The answer is to make staying with a receiver hard: send receivers across the field, where their defenders have to chase them through traffic; break away from a defender’s leverage; or find a defender who is badly matched against the man he has to cover.

Mesh

Mesh is the purest man-beater in football. Two receivers from opposite sides run shallow crossing routes, five or six yards deep, that pass within a yard or two of each other over the middle of the field. One runs his cross a step higher than the other, and Air Raid coaches tell them to pass close enough to slap hands.

Show the Python
p = Play(spread(rb_w=1.5), cover1(), los=LOS, to_go=3)
protect(p, rushers=("WDE", "WDT", "SDT", "SDE"))
p.rush("MIKE", pts=[(-4.0, 2.6)], speed=4.0)
p.dropback(2.5)
route_to(p, "H", (3.8, -9.0), (6.6, -6.0), (7.0, 9.5))
route_to(p, "Y", (2.6, 8.4), (5.2, 5.0), (5.8, -10.5))
p.route("X", r.corner(10.0))
p.route("Z", r.corner(10.0))
route_to(p, "RB", (-3.4, 5.0), (0.2, 9.5), (1.2, 12.0))
for d_, t_ in (("LCB", "X"), ("RCB", "Z"), ("NB", "H"), ("SS", "Y"), ("WILL", "RB")):
    p.man(d_, t_)
p.highlight("NB", "SS")
fld = plate(p, nums=[(1, (21.6, 21.2)), (2, (8.0, 10.6)), (2, (6.6, -12.4)), (3, (-2.6, 12.0))],
            keys=["route", "man", "num", "ring", "ltg"])
opening(fld, p, (6.3, 0.0), (2.6, 4.2))
say(fld, p, 10.0, -0.2, "the mesh point:\nclose enough to\nslap hands", color=OD)
tag(fld, p, "Man-beater (and zone holes)")
plt.show()
Figure 23.11: Mesh against Cover 1 (man coverage with one deep safety), third-and-3, the version run by the Air Raid. H and Y cross at five to six yards over the ball, H a step above Y, so the defenders chasing them, the nickel ($) and the strong safety (SS), both ringed, have to get around one receiver to stay with the other. X and Z run corner routes and the back runs to the right flat. The progression in the traditional Air Raid order: (1) the corner route on the back’s side, over his flat route; (2) the crossers, whichever comes out of the traffic first; (3) the back. Dotted lines are man assignments: the Will has the back, and the Mike rushes.

Against man coverage the two crossing paths do the work. The nickel chasing H has to get past Y coming the other way, and the safety chasing Y has to get past H. One of them almost always loses a step, and a receiver crossing the field with a one-step lead is nearly impossible to catch from behind. Against zone the same routes simply settle: each crosser finds the space between two underneath defenders and sits down facing the quarterback, so the concept is useful even when the defense guesses right. The corner routes give the quarterback a high-low against the cornerbacks, and keep the safety from crowding the middle.

Why would the Air Raid’s progression start with the corner route in the purest man-beater? Timing and payoff. The crossers need a beat to get through the traffic and come out the other side; the corner route over the back’s flat is ready first, and it is a high-low on whoever has the flat, so if it’s open it’s the biggest gain on the play. By the time the quarterback comes off it, one of the crossers has usually come free. Against a defense he knows is in man, many teams skip straight to the crossers, the low-to-high rhythm of drive.

Mesh came into the Air Raid from BYU, which, as Leach put it, ran the crossing pattern “for two decades” under LaVell Edwards, and it became one of the Air Raid’s signature plays;5 versions of it now appear in nearly every NFL playbook, often as a third-down call, when defenses play the most man coverage.

The rub, and where it becomes a pick

Mesh works because of a specific kind of route. A rub route (also called a pick route) is a route designed to cross close to a teammate’s route, so a defender chasing one receiver has to go around the other. Crossers, stacked receivers who release in opposite directions, and a slot receiver running a quick out under an outside receiver’s slant are all rubs. They are among the most effective plays in football against man coverage, especially near the goal line, where the field is short and defenders play tight.

They are also the most argued-about. The rules allow a receiver to run his route even if a defender runs into him. They don’t allow him to block a defender downfield until the pass is touched. In the NFL, a receiver who deliberately makes contact with a defender more than one yard past the line of scrimmage to get a teammate open, from the snap until the pass is touched, has committed offensive pass interference, a 10-yard penalty; contact within a yard of the line isn’t pass interference.6 That is the line between a rub and an illegal pick.

So is a pick inside that first yard legal? As far as pass interference goes, yes, and offenses use it: stacked and bunched receivers who release into each other right at the line are designed to make the contact happen there, before the one-yard limit. A receiver who grabs or shoves a defender can still be flagged for holding or illegal use of hands, but bumping him at the line on the way into a route is not a pick.

Show the Python
fig, axes = plt.subplots(1, 2, figsize=(6.5, 3.5))
for ax, legal in zip(axes, (True, False)):
    off = [O("H", "WR", OFF_BALL, -3.0), O("Y", "WR", OFF_BALL, 3.0), O("C", "C", LINE_D, 0.0)]
    dfn = [D("SS", "SS", 1.6, 2.4, "SS"), D("NB", "NB", 1.6, -2.4, "$")]
    p = Play(off, dfn, los=LOS, to_go=None)
    if legal:
        route_to(p, "H", (3.0, -3.0), (5.5, 0.0), (6.0, 6.5))
    else:
        route_to(p, "H", (3.0, -3.0), (4.0, -0.8))
    route_to(p, "Y", (2.5, 3.0), (4.6, 0.6), (5.2, -6.5))
    fld = draw(p, ax=ax, lateral=(-9.5, 9.5), window=(-3.5, 10.5), show_ruler=False, width_in=3.2)
    x0, y0 = _P(fld, p, 0.0, -9.5)
    x1, y1 = _P(fld, p, 1.0, 9.5)
    ax.add_patch(Rectangle((min(x0, x1), min(y0, y1)), abs(x1 - x0), abs(y1 - y0), fc=style.THIRD, alpha=0.18,
                           ec="none", zorder=1.5))
    if legal:
        branch(fld, p, [(1.6, 2.4), (3.2, 1.6), (4.4, 1.6), (3.6, -0.6), (4.4, -3.4), (4.8, -6.0)],
               color=style.DEFENSE, lw=1.4, dash=(0, (1, 1.6)))
        say(fld, p, 8.6, -4.0, "H runs his route;\nthe SS has to go\naround him", color=DD, fontsize=6.6)
        panel_title(ax, "Legal rub", fs=8.5)
    else:
        branch(fld, p, [(1.6, 2.4), (3.4, 1.4), (4.0, 0.2)], color=style.DEFENSE, lw=1.4)
        x, y = _P(fld, p, 4.1, -0.1)
        ax.plot([float(x)], [float(y)], marker="*", ms=13, color=DD, mec="white", mew=0.6, zorder=12)
        say(fld, p, 8.6, -3.6, "H stops and steps\ninto the SS 4 yards\ndownfield: flag", color=DD, fontsize=6.6)
        panel_title(ax, "Illegal pick (OPI)", fs=8.5)
    say(fld, p, -1.6, 6.4, "first yard:\nnot PI", color=READ_GREEN, fontsize=6.2)
fig.tight_layout(w_pad=1.0)
plt.show()
Figure 23.12: The same two routes, legal and illegal. Left: a legal rub. H runs his crossing route without breaking stride; the strong safety chasing Y has to go around him (dotted orange), loses a step, and Y comes free. No one blocked anybody. Right: an illegal pick. H stops his route four yards downfield, turns, and steps into Y’s defender before the pass is touched: that is offensive pass interference. The defenders match the mesh plate: the nickel ($) has H, the strong safety (SS) has Y. The green band is the first yard past the line of scrimmage, where contact between receivers and defenders isn’t pass interference in the NFL.

The trouble is that the difference is often a matter of intent and inches, and officials have to judge it at full speed. Offenses teach receivers to run through the defender’s path rather than at him, and to keep running; defenses teach their players to communicate (“switch” or “banjo,” trading receivers instead of chasing through the traffic, which Man Coverage explains) or to play zone, against which rubs gain nothing. How the officials enforce pass interference, and how often they call it, is in Contact Rules and Penalties.

Here is mesh against man coverage, moving. Watch the strong safety chasing Y (circled in brown).

Show the Python
def mesh_play():
    p = Play(spread(rb_w=1.5), cover1(), los=LOS, to_go=3)
    protect(p)
    p.rush("MIKE", pts=[(-4.0, 2.6)], speed=4.0)
    p.dropback(2.5)
    route_to(p, "H", (3.8, -9.0), (6.6, -6.0), (7.0, 9.5), (7.2, 14.0))
    route_to(p, "Y", (2.6, 8.4), (5.2, 5.0), (5.8, -10.5), (6.0, -16.0))
    p.route("X", r.corner(10.0))
    p.route("Z", r.corner(10.0))
    route_to(p, "RB", (-3.4, 5.0), (0.2, 9.5), (1.2, 12.0))
    p.drop("FS", (15.0, 0.0), speed=2.0)
    p.pass_to("Y", t=2.1)
    set_track(p, "NB", chase(p, "NB", "H", lag=0.1, off=(0.6, -0.8)))
    # the SS trails Y closely, then has to go over the top of H at the mesh point
    set_track(p, "SS", chase(p, "SS", "Y", lag=0.08, off=(-0.5, 0.5), detour=(1.6, 2.4, (8.3, 0.9)),
                             lag_after=0.35))
    set_track(p, "LCB", chase(p, "LCB", "X", lag=0.15, off=(-0.6, 0.6)))
    set_track(p, "RCB", chase(p, "RCB", "Z", lag=0.15, off=(-0.6, -0.6)))
    set_track(p, "WILL", chase(p, "WILL", "RB", lag=0.3, off=(1.6, -0.8), t_start=0.3, t_on=1.3))
    return p


mp = mesh_play()
with ball_speed(24.0):
    mt = catch_time(mp)
notes = ["Man coverage: $ on H, SS on Y, Will on\nthe back, one safety deep, Mike rushes.",
         "The mesh point: H and Y cross; the SS\nhas to go over the top of H.",
         "Ball out at 2.1 s: Y is about three\nyards clear, running away from the SS.",
         "Caught in stride, with open grass\nto the left and no one in front."]
with ball_speed(24.0):
    play_movie(mp, [0.0, 1.9, 2.1, mt], notes=notes, lateral=(-16.0, 14.0), window=(-9.0, 17.0),
               rings=(("SS", style.THIRD), ("Y", style.BALL)), throw_t=2.1)
Figure 23.13: Mesh against Cover 1, simulated (BDB-format tracking, generated by gridiron rather than recorded). H and Y cross at about six yards over the ball, H a step higher. The nickel ($) trails H on his hip, above the traffic. The strong safety (SS, ringed green), trailing Y (ringed brown) by about a yard, meets H at the mesh point and has to go over the top of him; by the time the ball is out, at 2.1 seconds, Y has about a three-yard lead and is running away from him toward the open left side, before the deep safety can come down. Faint lines are where each player has been; the brown dashes are the ball in flight.

The angle route: a running back against a linebacker

The other classic man-beater uses a mismatch instead of traffic. In man coverage, somebody has to cover the running back, and it is usually a linebacker (in these pictures, the Will): a player chosen for stopping runs, often thirty pounds heavier than the back and a step slower in space. The angle route (many playbooks call it the Texas route) is built for that matchup. The back releases toward the flat as if running a swing, and when the linebacker widens to chase him, he plants and snaps back inside, angling upfield under the middle of the field.

Show the Python
p = Play(spread(rb_w=1.5), cover1(extra={"WILL": (5.0, 0.2), "MIKE": (4.2, -2.6), "SS": (4.5, 7.2), "NB": (1.6, -7.4)}), los=LOS, to_go=7)
protect(p)
p.rush("MIKE", pts=[(-4.0, 3.4)], speed=4.0)
p.dropback(2.5)
route_to(p, "RB", (-3.6, 4.8), (-0.8, 7.5), (0.8, 7.2), (6.2, 3.4))
p.route("Y", r.seam(20.0))
p.route("Z", r.go(21.0))
for d_, t_ in (("LCB", "X"), ("RCB", "Z"), ("NB", "H"), ("SS", "Y"), ("WILL", "RB")):
    p.man(d_, t_)
p.highlight("WILL")
fld = plate(p, nums=[(1, (7.6, 2.0)), (2, (19.8, 10.6))],
            ghosts=[[(-0.7, -15.0), (2.3, -15.0), (6.5, -10.2)], [(-1.5, -9.0), (19.0, -9.0)]],
            keys=["route", "ghost", "man", "react", "num", "ring", "ltg"])
branch(fld, p, [(5.0, 0.2), (3.8, 3.8), (2.9, 6.2)], color=style.DEFENSE, lw=1.4)
say(fld, p, -4.6, 10.0, "release\nwide...", color=OD)
say(fld, p, 9.6, 6.4, "...snap back\ninside", color=OD)
tag(fld, p, "Man-beater: the linebacker")
plt.show()
Figure 23.14: The angle route against Cover 1, on the offense’s right, third-and-7. The back (R) releases wide toward the flat, then plants just past the line and breaks back inside and upfield, into the space the linebacker started in. The ringed Will (W), covering him man-to-man, widens with the release (dashed orange) and is left outside when the back cuts back. Y’s seam and Z’s go carry the strong safety and the cornerback deep, out of the area the angle route breaks into. The progression: (1) the angle route; (2) Y’s seam, one-on-one against the strong safety, if the Will doesn’t bite and stays inside. The Mike rushes. The left side (grey) runs a slant and a go.

The angle route is a cousin of the option route of Passing Fundamentals: the back is breaking away from his defender’s leverage. Against zone it still works if the routes outside clear the area, because the back settles in the hook zone the linebacker has left, though the throw is worth less. It is also why offenses love backs who can run routes: a back who wins on the angle route forces the defense either to cover him with a safety, which takes a defender out of the run fit, or to accept the mismatch.

Deep shots: four verticals, post-wheel, scissors and yankee

The last four concepts go deep. They exist because a defense that never has to worry about the deep ball will crowd every short and intermediate route in this chapter; somebody has to make the deepest defenders defend something. Each of them stretches the deep defenders, the safeties and the cornerbacks who play over the top, rather than the linebackers.

Four verticals

Four verticals is the simplest idea in the passing game: four receivers run straight up the field at once. The two outside receivers stay near the sideline, the two inside receivers run up the seams, a few yards outside the hashes. The defense has only two or three deep defenders, so somebody is one-on-one, or nobody is there at all.

Show the Python
p = Play(spread(rb_w=1.5), cover3(nb_side=1), los=LOS, to_go=10)
protect(p)
p.dropback(3.0)
for k in ("X", "Z"):
    p.route(k, r.path((10.0, 0.4), (24.0, 1.0)))
for k in ("H", "Y"):
    p.route(k, r.path((7.0, -1.4), (24.0, -3.0)))
route_to(p, "RB", (-3.6, 2.8), (4.0, 1.0), delay=0.8)
p.drop("FS", (17.0, 0.0))
p.drop("LCB", (17.0, -16.8))
p.drop("RCB", (17.0, 16.8))
p.drop("NB", (11.0, 9.0))
p.drop("SS", (11.0, -9.0))
p.drop("MIKE", (8.5, 3.0))
p.drop("WILL", (8.5, -4.0))
p.highlight("FS")
fld = plate(p, nums=[(1, (22.4, -8.4)), (1, (22.4, 8.4)), (2, (5.8, 0.0))],
            stretch="Horizontal stretch: the deep defenders")
bracket(fld, p, 20.0, -6.0, 6.0, "", color=DD)
say(fld, p, 18.2, 0.0, "one safety,\ntwo seams", color=DD)
plt.show()
Figure 23.15: Four verticals against Cover 3. X and Z run go routes outside the numbers; H and Y run up the seams just outside the hashes. Three deep defenders face four vertical routes: the cornerbacks have the outside receivers in their deep thirds, so the ringed free safety, alone in the middle third, has two seams to cover. The progression: (1) the seam he doesn’t take, read from his first steps; (2) the back, who checks for a blitzer and then settles over the ball, if the curl-flat defenders sink with the seams. The outside go routes are the first look only against a cornerback who sits short, as in Cover 2.

The arithmetic is the point. Against Cover 3, three deep defenders divide the field into thirds; four vertical routes put two receivers in the middle third, where there is only one defender. If he leans toward one seam, the quarterback throws the other. Against Cover 2, with two deep safeties each guarding a half, the seams split the safeties and the outside receivers attack the gap between the squatting cornerbacks and the safeties along the sideline, the same hole smash attacks. The concept forces the deep defenders to show where they are, and the quarterback reads them, not the underneath ones. The defense’s best answer is to have the curl-flat defenders run with the seams for the first several yards, so the safety only has to help on one of them; how Cover 3 defenses do that is a large part of The Cover 3 Family.

Show the Python
def verts_play():
    p = Play(spread(rb_w=1.5), cover3(nb_side=1), los=LOS, to_go=10)
    protect(p)
    p.dropback(3.0)
    for k in ("X", "Z"):
        p.route(k, r.path((10.0, 0.4), (28.0, 1.2)))
    for k in ("H", "Y"):
        p.route(k, r.path((7.0, -1.4), (28.0, -3.2)))
    route_to(p, "RB", (-3.6, 2.8), (4.0, 1.0), delay=0.8)
    p.drop("LCB", (25.0, -17.0), speed=5.5)
    p.drop("RCB", (25.0, 17.0), speed=5.5)
    p.path("FS", [(1.5, 0.6), (3.5, 4.0), (5.0, 6.4)], speed=4.2, delay=0.2)   # drifts with the QB's eyes
    p.path("FS", [(4.0, -6.5)], speed=6.5, delay=0.15)                         # breaks on the throw
    p.path("NB", [(6.5, -0.2)], speed=4.5)     # carries Y's seam a few steps, then lets him go
    p.path("SS", [(6.0, 0.4)], speed=4.5)      # the same with H
    p.drop("MIKE", (8.0, 3.0))
    p.drop("WILL", (8.0, -4.0))
    p.pass_to("H", t=2.2)
    return p


def qb_eyes(ax, fld, cur, t, play=None, until=2.15, look=(7.0, 6.0)):
    """A green arrow from the quarterback toward where he is looking, before the throw."""
    if play is None or not (0.3 <= t <= until):
        return
    qd, qw = play.position("QB", t)
    (x0, y0), (x1, y1) = fld.xy(*play.to_abs(qd, qw)), fld.xy(*play.to_abs(qd + look[0], qw + look[1]))
    ax.add_patch(FancyArrowPatch((float(x0), float(y0)), (float(x1), float(y1)), arrowstyle="-|>",
                                 mutation_scale=9, color=READ_GREEN, lw=1.4, ls=(0, (2, 1.5)), zorder=9,
                                 shrinkA=6, shrinkB=0))
    ax.text(float(x1), float(y1), " eyes", color=READ_GREEN, fontsize=6.5, ha="left", va="center",
            fontweight="bold", zorder=12)


vp = verts_play()
with ball_speed(24.0):
    vt = catch_time(vp)
notes = ["Three deep defenders (two CBs, FS)\nagainst four vertical routes.",
         "The CBs bail with X and Z; the seams\nrun past the curl-flat defenders.",
         "The QB has looked right; the FS drifted\nto Y. A beat later, the ball goes to H.",
         "Caught about 25 yards deep, in the\nmiddle third the FS left."]
with ball_speed(24.0):
    play_movie(vp, [0.0, 1.6, 2.1, vt], notes=notes, lateral=(-24.0, 24.0), window=(-10.5, 31.0),
               rings=(("FS", style.THIRD),), throw_t=2.2,
               deco=lambda ax, fld, cur, t: qb_eyes(ax, fld, cur, t, play=vp))
Figure 23.16: Four verticals against Cover 3, simulated (BDB-format tracking, generated by gridiron). The cornerbacks bail with the outside receivers, staying two or three yards on top of them; the curl-flat defenders run with the seams for a few steps and then let them go; and the free safety (ringed) has to choose. The quarterback looks to his right as he drops (the green arrow is his eyes), and the safety drifts that way, toward Y; the ball goes to H on the left seam, behind the curl-flat defender and in front of the cornerback, about 25 yards downfield. The safety breaks on the throw, too late.

Notice what the quarterback did with his eyes. He looked toward Y as he dropped, and the safety, who reads the quarterback, drifted that way. Moving a defender with your eyes is called a look-off, and it’s an essential skill for any concept that reads a single deep defender (Quarterback Play has more).

Four verticals has no single inventor. Its roots are usually traced to the vertical passing games of Sid Gillman and Don Coryell, to LaVell Edwards’s BYU and to the run-and-shoot offenses of Mouse Davis, all of which built their attacks around receivers running up the field; the Air Raid turned it into a signature call, with receivers taught to stay in their lanes but get open in them.7 In the modern NFL it rarely looks like four straight lines. The seam runners usually carry a read, holding straight against one deep safety and bending inside between two (a route conversion, as below), and offenses with speed outside, like Andy Reid’s Chiefs with Tyreek Hill, use the outside go routes to push the cornerbacks off.

Post-wheel

Post-wheel splits the defenders on one side, inside and out. The outside receiver runs a post, angling toward the middle at about 12 yards; the inside receiver (or a back) runs a wheel, out toward the flat and then straight up the sideline, into the space the post receiver just left. It is read high and outside first: the wheel.

Show the Python
p = Play(spread(), cover3(nb_side=1), los=LOS, to_go=10)
protect(p)
p.dropback(3.0)
route_to(p, "Z", (10.0, 15.0), (19.5, 7.5))
route_to(p, "Y", (-0.8, 11.5), (1.5, 14.5), (5.5, 18.5), (21.0, 19.4))
rb_n = swing(p, -1)
p.drop("NB", (6.0, 13.6))
p.drop("FS", (16.0, 0.0))
p.drop("MIKE", (8.0, 3.0))
p.drop("WILL", (8.0, -4.0))
p.drop("SS", (7.0, -12.0))
p.drop("LCB", (17.0, -16.6))
p.highlight("RCB", "NB")
fld = plate(p, nums=[(1, (21.0, 21.9)), (2, (21.2, 7.0)), (3, rb_n)],
            ghosts=[[(-0.7, -15.0), (11.3, -15.0), (11.3, -5.0)], [(-1.5, -9.0), (20.0, -7.6)]],
            stretch="Horizontal stretch: corner and $",
            keys=["route", "ghost", "drop", "react", "num", "ring", "ltg"])
branch(fld, p, [(7.0, 16.0), (11.5, 15.6), (16.5, 11.0)], color=style.DEFENSE, lw=1.4)
say(fld, p, 13.5, 22.4, "wheel", color=OD)
say(fld, p, 12.4, 9.0, "post", color=OD)
plt.show()
Figure 23.17: Post-wheel to the offense’s right against match Cover 3, a version many NFL defenses now play. Z runs a post; Y, from the slot, runs a wheel, out toward the flat and then up the sideline. The two ringed defenders are the pair in conflict. The cornerback’s rule is to carry the outside receiver when he goes deep, so the post drags him inside (dashed). That hands the wheel to the nickel ($), the curl-flat defender, who widened with Y’s release from five yards deep and can’t turn and run with him up the sideline. Progression: (1) the wheel; (2) the post, if the cornerback passes Z on to the free safety and stays outside; (3) the back on the left. H’s seam (grey) holds the free safety in the middle.

Why does it work? Because each defender on that side has a sensible rule, and the two routes make the rules collide. Many NFL defenses now play match Cover 3: instead of dropping to a spot, each defender picks up a receiver once the routes declare. The cornerback’s rule is to carry the outside receiver if he goes deep, so when Z pushes vertical and bends to the post, the cornerback goes inside with him. The curl-flat defender’s rule is to take the second receiver if he goes to the flat. The wheel starts as exactly that, a harmless flat route, and then turns up the sideline, where a nickel who started five yards deep, facing the quarterback, has to turn and run with a receiver who already has a head of steam. The wheel is behind him and outside the cornerback, and nobody’s rule covers it in time.

If the cornerback refuses to be dragged inside (some teams teach him to pass the post on to the free safety and stay outside for anything up the sideline), the post is the throw: now it’s a one-on-one inside against the free safety, whom the backside seam has held in the middle. We file post-wheel as a horizontal stretch because that is its shape: two deep routes, one breaking inside and one outside, splitting the defenders’ areas side to side. Against man coverage it is better still: the wheel puts a slot receiver or a running back against a nickel or a linebacker who has to turn and run up the sideline with him.

Scissors

Scissors is post-wheel’s two-high cousin. The outside receiver still runs a post, but the inside receiver runs a corner route instead of a wheel, so their paths cross at 10 to 12 yards like the blades of a pair of scissors. It is read inside first: the post.

Show the Python
p = Play(spread(), quarters(extra={"SS": (10.5, 6.6), "FS": (10.5, -6.6)}), los=LOS, to_go=10)
protect(p)
p.dropback(3.0)
route_to(p, "Z", (9.3, 15.0), (19.5, 5.0))
route_to(p, "Y", (8.5, 9.0), (18.5, 18.6))
rb_n = swing(p, -1)
p.drop("NB", (8.5, 10.0))
p.drop("FS", (15.0, -8.0))
p.drop("MIKE", (8.0, 3.0))
p.drop("WILL", (8.0, -5.0))
p.drop("LCB", (15.0, -16.0))
p.highlight("SS", "RCB")
fld = plate(p, nums=[(1, (21.2, 3.4)), (2, (20.0, 20.6)), (3, rb_n)],
            ghosts=[[(-0.7, -15.0), (11.3, -15.0), (11.3, -5.0)], [(-1.5, -9.0), (5.0, -9.0), (5.2, -7.0)]],
            stretch="Horizontal stretch: safety and corner",
            keys=["route", "ghost", "drop", "react", "num", "ring", "ltg"])
branch(fld, p, [(10.5, 6.6), (13.4, 7.8), (16.2, 14.0)], color=style.DEFENSE, lw=1.4)
branch(fld, p, [(7.5, 16.0), (11.0, 16.2), (15.6, 11.0)], color=style.DEFENSE, lw=1.4)
point_to(fld, p, (17.6, -1.0), (12.0, 12.4), "the blades cross\nat about 12 yards", color=OD)
plt.show()
Figure 23.18: Scissors to the offense’s right against Quarters. Z runs a post, Y a corner route; their paths cross at about 12 yards. The two ringed defenders both lose their leverage (dashed): the strong safety, who starts inside Y and carries him when he goes vertical, has to cross the field outside to follow the corner route; the cornerback, who starts outside Z, has to cross inside to follow the post. Their paths cross too, a rub at depth. The progression: (1) the post, inside the cornerback and away from the safety; (2) the corner route, if the safety passes Y on and stays inside; (3) the back, checking down on the left.

Scissors beats Quarters by turning its leverage inside out. In Quarters, as you saw with dagger, the safety reads the inside receiver from inside leverage and carries him if he goes vertical, and the cornerback plays the outside receiver almost man-to-man from outside leverage. Each defender starts on the side of his receiver that the receiver is least likely to break toward. Scissors sends both receivers the other way. Y goes vertical, so the safety carries him, and then Y breaks outside: the safety has to cross the field toward the sideline, through Z’s path, from the wrong side. Z breaks inside on the post: the cornerback has to cross inside, through Y’s path, from the wrong side. Both defenders lose their leverage at once, and the crossing paths add a rub at depth. Against man coverage the same crossing makes it a man-beater, too.

Yankee

Yankee is a two-man deep shot. One receiver runs a post from one side; the other runs a deep over route, a crosser at about 18 to 22 yards, from the other side. Both routes finish in the deep middle, one above the single deep safety and one in front of him. It is usually run off play-action, with a fake handoff to freeze the linebackers, and with max protection, seven or eight blockers, because both routes take three seconds or more to develop.

Show the Python
off = line5() + [O("QB", "QB", UNDER, 0.0, "Q"), O("RB", "RB", -7.0, 0.0, "R"),
                 O("Y", "TE", LINE_D, 4.8), O("U", "TE", LINE_D, -4.8, "U"),
                 O("X", "WR", OFF_BALL, -15.0), O("Z", "WR", OFF_BALL, 15.0)]
dfn = dline() + [D("SAM", "SAM", 4.0, 5.8, "S"), D("MIKE", "MIKE", 4.5, 0.6, "M"), D("WILL", "WILL", 4.5, -3.4, "W"),
                 D("SS", "SS", 8.0, -7.0, "SS"), D("LCB", "CB", 7.0, -16.0, "CB"), D("RCB", "CB", 7.0, 16.0, "CB"),
                 D("FS", "FS", 13.5, 0.0, "FS")]
dfn = [q.moved(w=-6.6) if q.key == "WDE" else q.moved(w=6.6) if q.key == "SDE" else q for q in dfn]
p = Play(off, dfn, los=LOS, to_go=10)
protect(p)
p.block("Y", pts=[(-1.0, 0.0)])
p.path("QB", [(-2.6, 1.2)], speed=4.5)
p.path("QB", [(-6.2, 0.0)], speed=4.0)
p.run("RB", [(1.0, 1.2), (2.0, 1.6)], speed=5.0)
route_to(p, "X", (10.5, -15.0), (25.0, -3.0))
route_to(p, "Z", (6.0, 13.5), (16.0, 4.0), (18.4, -12.0))
route_to(p, "U", (-0.4, -6.0), (1.4, -10.0), delay=1.4)
for k, to in (("SAM", (3.0, 4.6)), ("MIKE", (3.4, 1.2)), ("WILL", (3.4, -2.4))):
    p.path(k, [(to[0] - p.players[k].d, to[1] - p.players[k].w)], speed=3.0)
p.drop("FS", (19.5, -0.5))
p.drop("LCB", (19.5, -17.0))
p.drop("RCB", (17.0, 16.6))
p.drop("SS", (8.0, -11.0))
p.highlight("FS")
fld = plate(p, nums=[(1, (26.4, -1.6)), (2, (16.4, -13.6)), (3, (3.0, -11.8))],
            keys=["route", "run", "drop", "num", "ring", "ltg"], window=(-8.5, 29.0),
            stretch="Vertical stretch: the deep safety")
say(fld, p, 25.8, -11.6, "post over the top", color=OD)
say(fld, p, 13.6, 9.6, "deep over\nunder the FS", color=OD)
say(fld, p, -6.6, 5.0, "run fake", color=OD)
plt.show()
Figure 23.19: Yankee against Cover 3, from an under-center formation with two tight ends (12 personnel). Against two tight ends the defense stays in its base personnel, with a third linebacker, the Sam (S), instead of a nickel. The quarterback fakes a handoff to the back (R), then sets up deep. X runs a post from the left; Z runs a deep over from the right, climbing to about 18 yards and finishing near the far numbers. The ringed free safety, alone in the deep middle, faces a high-low, read high to low: (1) the post if he comes up on the over; (2) the over if he stays deep with the post. The over is thrown in front of the left cornerback, who has to stay deep and outside in his third, and behind the linebackers, who step up toward the run fake (short orange arrows). The five linemen, Y and the back block (seven-man protection); U helps for a moment, then releases late to the flat as the outlet (3).

Why does it work? The run fake does half the job. Linebackers are taught to stop the run first, so they step forward when they see a handoff, and that step takes them out of the path of the deep over, which crosses behind them at a depth no underneath defender can reach. The other half is a high-low on the one man left: the free safety in the middle of the field can sink to take away the post or come forward to take away the over, not both. The post keeps him honest, and the over is thrown where nobody else can reach it: in front of the deep-third cornerback, who can’t leave his deep outside area, and behind linebackers who have just stepped toward a handoff. Sean McVay’s Rams made yankee one of their signature shot plays of the late 2010s (the Rams often ran it from three-receiver sets with jet motion), Kyle Shanahan’s 49ers ran it too, and it now appears in most NFL playbooks that come from the Shanahan and McVay family of offenses.8 The price is the protection: with seven or eight blockers, only two or three receivers go out, so if the safety plays it perfectly the quarterback has very little to throw to. How play-action works, and why the fake works even when the run game doesn’t, belongs to Play-Action, Bootlegs, and Screens.

Tags and route conversions

Fourteen concepts sounds like a lot, but a real playbook has many more, and most of the extras aren’t new concepts at all. They are the same concepts with a word added. A tag is a word added to a play call that changes one player’s assignment and leaves everyone else’s alone. “Mesh” tells all five receivers what to do; “Mesh, Z-wheel” keeps mesh intact except for Z, who runs a wheel instead of his corner route. Tags let a play-caller adjust a concept to a defense’s habits (a cornerback who jumps corner routes, a safety who cheats toward the crossers) without teaching a new play.

The second tool is subtler, and it explains how one call can be right against several coverages. A route conversion is a rule built into a route that changes it according to the coverage the receiver sees. The receiver doesn’t wait for the quarterback to tell him; he reads the defense himself, usually the safeties, and the quarterback, reading the same thing, knows where he will be.

Show the Python
fig, axes = plt.subplots(1, 2, figsize=(6.5, 3.7))
for ax, one_high in zip(axes, (True, False)):
    off = [O("Y", "TE", OFF_BALL, 8.0), O("C", "C", LINE_D, 0.0)]
    if one_high:
        dfn = [D("FS", "FS", 13.5, 0.0, "FS")]
    else:
        dfn = [D("FS", "FS", 12.5, -8.5, "FS"), D("SS", "SS", 12.5, 8.5, "SS")]
    p = Play(off, dfn, los=LOS, to_go=None)
    if one_high:
        route_to(p, "Y", (8.0, 7.8), (22.0, 9.0))
        p.drop("FS", (17.0, 0.0))
    else:
        route_to(p, "Y", (11.0, 7.6), (21.0, 1.2))
        p.drop("FS", (16.0, -10.0))
        p.drop("SS", (16.0, 10.0))
    p.highlight(*[q.key for q in dfn])
    fld = draw(p, ax=ax, lateral=(-13.5, 15.5), window=(-3.5, 24.5), show_ruler=False, width_in=3.2)
    if one_high:
        say(fld, p, 22.6, -6.0, "one safety:\nstay on the\nlandmark", color=OD, fontsize=6.8)
        panel_title(ax, "One high (MOFC): stay vertical", fs=8.5)
    else:
        opening(fld, p, (20.0, 1.0), (4.0, 7.0))
        say(fld, p, 9.0, -6.0, "two safeties:\nbend into the\nempty middle", color=OD, fontsize=6.8)
        panel_title(ax, "Two high (MOFO): bend inside", fs=8.5)
fig.tight_layout(w_pad=1.0)
plt.show()
Figure 23.20: One route conversion: the seam route in four verticals. The receiver (Y) reads the safeties as he runs. Left, one deep safety (middle of the field closed, MOFC): he stays on his landmark just outside the hash and bends slightly away from the safety, keeping him in conflict with the other seam. Right, two deep safeties (middle of the field open, MOFO): the middle is empty, so at about 12 yards he bends inside, splitting the safeties, into the space between them (green). Same call, same receiver; the coverage picks the route.

You’ve already met the most important conversion. The option route of Passing Fundamentals, which some systems call the choice route, is a conversion with two branches: against man, break away from the defender’s leverage; against zone, sit in the hole. Others are everywhere in this chapter. The smash corner route goes to depth over a squatting Cover 2 cornerback and flattens under a bailing Cover 3 one. The levels and mesh crossers keep running against man and settle against zone. The dagger dig sits down in a window against zone and keeps crossing against man. And the Y-cross, the Air Raid’s most conversion-heavy route, keeps running toward the far sideline against Cover 3 and man, and sits down in the window against two deep safeties or a robber lurking in the middle.

One caution about names while we’re here. Teams name the same concept differently (you’ve already met flood and sail, and levels as the Colts’ “Dig”), and some reuse a name for a different concept. The shape is what to learn; a name only tells you what one team calls it.

The Patriots of the Tom Brady era were the model of an offense built on conversions. Their Erhardt-Perkins system (named for Ron Erhardt and Ray Perkins, who coached together on New England’s staff in the 1970s, and carried forward by Bill Parcells to Bill Belichick and Josh McDaniels) named whole concepts with short words, so a quarterback could call them at the line.9 It also gave its slot receivers wide freedom to read and adjust their routes after the snap: the option routes of Troy Brown, Deion Branch, Wes Welker and Julian Edelman, which broke one way against man and sat down against zone, were a staple of the Brady years.10 That’s why New England’s passing game seemed to have an answer for every defense without a thousand plays: the same few concepts, each with branches. The system’s history and its rivals are in Offensive Systems and Languages.

NoteMadden vs. real life

In a video game, the receiver runs the route drawn on the play art, whatever the defense does, and you press a button for the one you want. In the NFL, a large share of routes are conditional: the receiver converts the route against the coverage, the quarterback has to read the same thing and throw to where the receiver will be, and a mismatch between their reads is one of the commonest causes of an interception that looks, on the broadcast, like the quarterback threw to nobody. When an announcer says a receiver and quarterback “weren’t on the same page,” that’s usually what happened: one of them saw man and the other saw zone.

The quarterback’s side: one clock for every concept

Each plate in this chapter carried a progression, and they all follow the same pattern. That pattern is the real skill of dropback passing, so it is worth spelling out.

Before the snap, the quarterback identifies the shell (one high safety or two), checks the leverage of the defenders over the receivers in the concept, and asks the question every concept poses: who is the defender in conflict, and where is he? If the concept is called to both sides, as smash often is, this is when he picks the side. And if what he sees is wrong for the call (a zone-beater against a defense that is plainly in man, say), many offenses let him change it: the play comes in as a pair of concepts, or with a check-with-me, and he picks at the line (The Pre-Snap Phase). The pre-snap picture is still only a hypothesis; the first step of the safeties after the snap confirms or breaks it (Disguise, Rotation, and Split-Field Coverage).

If the defense shows a blitz, he checks the protection (Pass Protection). Every dropback concept carries its own pressure answer: if the defense brings one more rusher than the protection can block, the concept’s quickest route becomes the hot route, the throw that has to come out before the free rusher arrives. On drive and mesh that is a shallow crosser, which is open almost at once against a blitz; on smash, flood or dagger it is usually the shortest route (the hitch, the flat) or the back releasing from the backfield.

At the snap, his eyes go to the conflict defender, not to a receiver. His drop is timed so that the first read is ready as his back foot lands: on smash, the corner route breaks as he hits the bottom of his drop; on dagger, the dig arrives a beat later, which is why dagger takes a deeper drop or a hitch step.

After the first look, he moves to the next read with his feet as well as his eyes. Each hitch step, a short step up into the pocket, resets his feet for the next throw. A typical dropback progression gets two or three looks before the rush arrives; the last is the checkdown, the short throw to a back that keeps the play from losing yards. As Passing Fundamentals showed with FTN’s charting, more than half of all throws go to the first read, and those are the quickest and most valuable: the concept is at its best when its first answer is right.

The order of the looks follows the two rhythms from the start of the chapter: high to low on most vertical stretches, low to high when a shallow crosser wins early or not at all, and on a long third down, the route that reaches the line to gain first.

The table gathers the fourteen concepts, the family each belongs to, the defender it puts in a bind and the coverage it’s built to beat. The last column is the subject of What Beats What, which pairs concepts with coverages in detail; how each coverage defends these concepts is in Part 6, beginning with Coverage Fundamentals and, for four verticals, The Cover 3 Family.

Table 23.1: The fourteen dropback concepts at a glance. “Built to beat” is the concept’s best matchup, not its only one.
Concept Family Defender in conflict Built to beat Typical progression
Smash Vertical stretch Cornerback Cover 2 corner → hitch → back
Flood (sail) Vertical, three levels Curl-flat defender Cover 3 sail → flat (go if the corner sits)
Curl-flat Triangle Curl-flat and hook defenders Cover 3, zone curl → flat → hook
Levels Vertical stretch Hook linebacker Zone dig → in → back
Drive Vertical; also a man-beater Middle linebacker Zone and man shallow → dig → back
Shallow cross Vertical; also a man-beater Middle linebacker Zone and man shallow → dig or sit → back
Dagger Vertical stretch Safety, then the cornerback Two-high (Quarters, Cover 2) seam → dig → back
Y-cross Vertical, three levels Far curl-flat defender Cover 3, man cross → flat → back
Mesh Man-beater Man defenders on the crossers Man (Cover 1, Cover 0) corner → crossers → back
Angle (Texas) Man-beater Linebacker covering the back Man angle → seam
Four verticals Horizontal, deep Free safety Cover 3, Cover 2 seam away from the safety → back
Post-wheel Horizontal, deep Cornerback and curl-flat defender Match Cover 3, man wheel → post → back
Scissors Horizontal, deep Safety and cornerback Quarters post → corner → back
Yankee Vertical, deep Free safety One-high post → over → outlet

Two honest limits on this table. First, you usually can’t tell these concepts apart before the snap, and that is by design: smash, flood and post-wheel all start as a slot and an outside receiver on one side, and an offense that tipped which one was coming would be easy to defend. What you can predict before the snap is the family, from the shell (two-high invites smash, dagger and scissors; one-high invites flood, four verticals, post-wheel and yankee; man invites mesh and the angle route) and from the down and distance. The concept itself reveals itself in the first two seconds, when the routes break. Second, nobody publishes how often each concept is called. Public play-by-play records where the ball went, not what was drawn; the Big Data Bowl tracking data at the end of this chapter charts each receiver’s route, which is enough to find concepts by their route combinations if you want to count them yourself.

Film room

The concepts above are in almost every NFL playbook. A few teams built an identity on them, and watching those teams is the fastest way to learn to see the concepts at game speed.

NoteFilm room: Texas Tech 2007–2008

Mesh and Y-cross, rep after rep. Mike Leach’s Texas Tech teams were the Air Raid at full volume. In 2007, quarterback Graham Harrell threw for 5,705 yards and redshirt freshman receiver Michael Crabtree caught 134 passes for 1,962 yards and 22 touchdowns; Crabtree won the Biletnikoff Award as the nation’s best receiver that season and again in 2008.11 Nearly all of it came out of the same small menu of concepts drawn in this chapter: mesh, shallow cross, Y-cross and four verticals, from wide 2x2 and trips formations with enormous splits between the offensive linemen.

What to notice on the tape: the mesh point, two crossers passing over the center close enough to touch, and a defender losing a step as he goes around; Harrell’s eyes on the curl-flat defender on Y-cross, and the ball arriving as the crosser came into the window; and how few different plays there were. The defense knew what was coming, and it didn’t matter, because the offense had run each concept thousands of times against every look.

One game to find: Texas Tech’s 39–33 win over No. 1 Texas on November 1, 2008, the school’s first win over a top-ranked team. Trailing 33–32 with 1:29 left, Harrell drove the Red Raiders 62 yards in six plays, and Crabtree caught the winning touchdown at the sideline with one second left.12 The final drive is famous; the rest of the game is the small menu at work.

Leach’s former quarterback Kliff Kingsbury later brought the same concepts to the NFL as the Arizona Cardinals’ head coach (2019–2022), and in 2026 joined Sean McVay’s Rams staff as assistant head coach.13

NoteFilm room: Los Angeles Rams 2018

Yankee and the play-action shot. In Sean McVay’s second season, the Rams went 13–3 and reached Super Bowl LIII, where they lost to New England 13–3. Jared Goff threw for 4,688 yards and 32 touchdowns.14 Their passing game ran on a single foundation: line up in the same formation with three receivers, run outside-zone handoffs out of it, and throw play-action shots from the same look, yankee among them. On throws of 20 yards or more in the air, the Rams averaged about +0.77 EPA per attempt, third-best in the league, against a league average of about +0.37.15

What to notice on the tape: the linebackers’ first step on the run fake, toward the line; the deep over crossing behind them at 18 to 20 yards; and the free safety in the middle, who has to decide between the over in front of him and the post behind him. Goff often had only two routes to choose from, and his decision was made by one defender.

One game to find: the Rams’ 54–51 win over the Chiefs on Monday night, November 19, 2018, in Los Angeles, when Goff threw for 413 yards and four touchdowns and Mahomes for 478 yards and six.16 Both offenses’ play-action and deep shots are on display; it is the companion game for the next film room too.

NoteFilm room: Kansas City Chiefs 2018

Four verticals with a 50-touchdown quarterback. In Patrick Mahomes’s first season as the starter he threw for 5,097 yards and 50 touchdowns and was named the league’s MVP. Andy Reid’s offense threw deep more than almost anyone: 15% of the Chiefs’ attempts traveled 20 yards or more in the air (fifth in the league; the league average was 12%), and those throws were worth about +0.75 EPA each, fourth best.17 With Tyreek Hill’s speed outside and Travis Kelce running up the seam, vertical routes made every deep defender declare early, which is the idea four verticals is built on.

What to notice on the tape: the deep defenders’ first steps. With Hill on the outside, cornerbacks bailed at the snap, which opened everything underneath; with Kelce on a seam, the middle safety had to choose. Watch the seam routes for a conversion, bending or staying straight as the receiver reads the safeties, and Mahomes moving the safety with his eyes before throwing the other way. The November 19 game against the Rams (above) is the place to start: Mahomes threw six touchdowns and three interceptions, a good lesson in what deep shots cost as well as what they pay.

Watch for it on Sunday

TipWatch for it: name the concept

Before the snap

  • Find the shell. One deep safety or two? Smash, dagger and scissors are two-high beaters; flood, post-wheel and yankee feast on one-high; four verticals is built for Cover 3 and still splits Cover 2’s safeties. (Can’t see the safeties on the TV angle? See How to Watch a Broadcast.)
  • Man or zone? Pressed corners, a defender following a motion man across the formation: think man, and think mesh, rubs and the angle route.
  • Check the situation. On third-and-long, expect a concept with a route past the line to gain; on third-and-short against man, expect crossers and rubs. You are predicting a family, not a name: from the same formation, smash, flood and post-wheel look identical until the routes break.

On the replay (the end-zone camera or the skycam is best)

  • Find the defender in conflict first. Who stood between the routes on the side the ball went to? What did he do? The ball usually went to the receiver he didn’t take.
  • Then name the stretch by where the routes were around him when the ball came out. One above and one below him: a vertical stretch (even if the routes crossed on the way there, as in drive or Y-cross). One on each side: horizontal. Three around two defenders: a triangle. No zone defender to stretch at all, with receivers running away from men chasing them through traffic: a man-beater. Then name the concept.
  • Count the time. A dropback concept takes about “one-thousand-one, one-thousand-two, one-thousand-three.” If the ball is out before “two,” it was the quick game.

For your prediction log (How to Watch a Broadcast): on each third-and-medium, write the shell you see and the concept family you expect (two-high: smash or dagger; one-high: flood or a deep shot; man: mesh or rubs). Check yourself on the replay.

Predict the play

TipPredict the play 1: two high, but which two-high?
Show the Python
p = Play(spread(), quarters(), los=55, to_go=9)
fld = draw(p, lateral="full", window=(-8.5, 17.5))
point_to(fld, p, (14.6, 0.0), (10.0, 7.5), "safeties at 10,\nover the slots", color=DD)
point_to(fld, p, (13.0, -19.0), (7.5, -16.0), "corners off", color=DD)
plt.show()
Figure 23.21: Third-and-9. Both safeties are only about ten yards deep, each lined up over a slot receiver; the cornerbacks are seven or eight yards off, with outside leverage; the nickel and the linebackers are at their usual depths.

The call sheet for this down has smash and dagger, both to the right. Which one should the offense run against this picture, and why not the other?

Dagger. Two high safeties, yes, but this isn’t the Cover 2 that smash was built for. Cover 2’s safeties sit 12 to 14 yards deep and wide, and its cornerbacks squat at five yards. Here the safeties are low and over the slots and the cornerbacks are off: that’s the Quarters look, where each safety reads the slot receiver in front of him.

Against Quarters, smash runs into trouble on both routes. The corner route is the slot going vertical and then outside, which is exactly what the safety is reading: he carries it from inside, and the cornerback, playing deep and outside over the hitch, is above it, so the corner route is bracketed. The hitch underneath is open against an off cornerback, but it’s five or six yards on third-and-9: short of the sticks, and it needs yards after the catch.

Dagger turns the same safety rule against him. Y’s seam takes the safety deep; the nickel and the Mike sink under it; and the dig comes in behind them against a cornerback playing from outside, at 15 yards, past the line to gain. What would make the quarterback come off it? A safety who doesn’t carry the seam (then throw the seam), or a rotation at the snap to one deep safety (then the seam attacks the middle safety, and the dig crosses under him).

TipPredict the play 2: press man on third down
Show the Python
p = Play(spread(rb_w=1.5), cover1(), los=72, to_go=3)
fld = draw(p, lateral="full", window=(-8.5, 17.5))
for d0, col, dash in ((-2.6, style.OFFENSE, (0, (3, 2))), (2.6, style.DEFENSE, (0, (1, 1.6)))):
    (x0, y0), (x1, y1) = _P(fld, p, d0, -8.3), _P(fld, p, d0, 2.6)
    fld.ax.annotate("", (float(x1), float(y1)), xytext=(float(x0), float(y0)), zorder=6,
                    arrowprops=dict(arrowstyle="<|-|>", color=col, lw=1.3, ls=dash, mutation_scale=9))
say(fld, p, -3.6, -14.4, "H's motion:\nacross and back", color=OD, fontsize=6.6)
say(fld, p, 6.8, -12.0, "the $ went\nwith him", color=DD, fontsize=6.6)
say(fld, p, 16.0, 0.0, "one deep safety", color=DD)
plt.show()
Figure 23.22: Third-and-3. Both cornerbacks are pressed on X and Z and the nickel ($) is pressed on H; the strong safety stands five yards off Y; the Will is over the back; one safety is deep in the middle. Before the snap, H went in motion across the formation and back (blue dashes), and the nickel ran across with him and back again (orange dots).

The offense has three concepts on its call sheet for this situation: flood, four verticals and mesh. Which one should it run, and why?

Mesh. Everything here says man coverage: pressed cornerbacks, a defender over every receiver, a single safety deep, and, the clincher, a nickel who followed H across the formation and back on the motion. Zone defenders don’t travel with a motion man; man defenders do (Motion and Shifts).

Against man, the zone-beaters lose their point. Flood’s three levels stretch a curl-flat defender who doesn’t exist; each receiver just has a defender following him. Four verticals is a better man answer but needs three seconds and a receiver who can beat a pressed cornerback deep, which is a big ask on third-and-3. Mesh is built for this: the two crossers run at five or six yards, past the line to gain, and the defenders on H and Y have to fight through each other’s traffic. One of the crossers usually comes free within about two seconds, running away from his man.

How does the offense act on what it saw? If the play called in the huddle was flood, a quarterback with a check-with-me or a paired call can switch to mesh at the line; that’s exactly what motion is for. What should still worry him? The Mike linebacker. In Cover 1, the defender not assigned to anyone often sits in the middle as a robber, exactly where the crossers meet. The quarterback has to find him before the snap and throw the crosser running away from him.

TipPredict the play 3: a concept you haven’t seen
Show the Python
p = Play(spread(), cover3(nb_side=1), los=40, to_go=10)
protect(p)
p.dropback(3.0)
route_to(p, "Z", (10.0, 15.0), (23.0, 4.0))
route_to(p, "Y", (13.5, 9.0), (14.5, -4.0))
p.drop("FS", (17.0, 0.0))
p.drop("RCB", (17.0, 16.6))
p.drop("LCB", (17.0, -16.6))
p.drop("NB", (8.0, 12.0))
p.drop("SS", (7.0, -12.0))
p.drop("MIKE", (8.5, 3.0))
p.drop("WILL", (8.5, -4.0))
fld = draw(p, lateral="full", window=(-8.5, 27.5))
ghost(fld, p, [(-0.7, -15.0), (22.0, -15.0)])
ghost(fld, p, [(-1.5, -9.0), (11.5, -9.0), (10.3, -7.8)])
plt.show()
Figure 23.23: Second-and-10, against Cover 3. On the right, Z runs a post and Y, from the slot, runs a dig at about 14 yards underneath it. On the left, X and H run a go and a curl (grey). The back checks for a blitzer. This exact combination isn’t drawn anywhere in the chapter.

Which family is it, who is the defender in conflict, what coverage does it want, and in what order should the quarterback read it?

A vertical stretch (a high-low) of the free safety, built to beat one-high coverage, read high to low: the post, then the dig, then the back. Both routes break toward the middle, which can make it look like a horizontal stretch or a crossing man-beater. Ask where the routes are around the key defender when the ball comes out: the post is above the free safety, heading for the deep middle he guards, and the dig is below him, crossing at 14 yards, above the linebackers. One defender, two depths.

That makes it yankee’s logic from one side of the formation, without the run fake: if the safety sinks for the post, the dig comes open in front of him; if he comes up for the dig, the post is behind him. Against two deep safeties, the middle is open and the post has nobody to stretch, so an offense would rather have this against a one-high shell (or with a conversion that bends the post away from a second safety).

Takeaways

  • A pass concept is a set of routes built to work together, usually by putting one or two defenders in a bind; it has a name so the whole offense can learn and call it as a unit, from any formation.
  • Concepts come in four families, named by where the routes are around the key defender when the ball comes out: vertical stretches (one above him, one below: smash, flood, levels, drive, shallow cross, dagger, Y-cross, yankee), horizontal stretches (one on each side, deep: four verticals, post-wheel, scissors), triangles (three receivers around two defenders: curl-flat, snag) and man-beaters (traffic and leverage against man defenders: mesh, the angle route).
  • Each concept is built to beat certain coverages: smash, dagger and scissors attack two-high; flood, post-wheel and yankee attack one-high; four verticals is built for Cover 3 and still splits Cover 2; mesh and the angle route attack man. That is why the quarterback’s pre-snap read starts with the safeties.
  • The quarterback reads the defender in conflict, not the receivers, usually high to low (low to high when a shallow crosser leads), and his progression runs on his feet: the first read as his back foot lands, the next on a hitch step, the checkdown last.
  • Rub routes beat man coverage by making defenders run through traffic; a receiver who deliberately blocks a defender more than a yard downfield, until the pass is touched, commits offensive pass interference.
  • Tags change one route in a concept; route conversions let a route change itself by coverage, so one call can be right against several defenses. The intermediate middle of the field is the most valuable place to throw, and most of these concepts exist to get the ball there.

Connections

Builds on: The Quick Game (concepts, leverage, the pre-snap read and the short stretches this chapter takes deeper), Passing Fundamentals (routes, drops, progressions and the two stretches) and Pass Protection (the time these concepts need).

Sets up: Play-Action, Bootlegs, and Screens runs flood and yankee off a run fake; Quarterback Play covers progressions, the pocket and look-offs in depth; Offensive Systems and Languages shows how the West Coast, Erhardt-Perkins, Air Raid and Shanahan systems name and call these concepts. On defense, Coverage Fundamentals, Man Coverage and The Cover 3 Family show the other side of each matchup; What Beats What pairs every concept with the coverages it beats; and Contact Rules and Penalties covers how officials judge rubs and picks.

Case study: Walsh’s 49ers and the West Coast offense, the offense that made curl-flat, the triangle read and the timed dropback game famous.

NoteGo deeper
  • Chris B. Brown, The Essential Smart Football (2012) and The Art of Smart Football (2015), and his Smart Football site: the clearest writing anywhere on smash, flood, mesh, Y-cross, levels and four verticals, and on why they work.
  • S. C. Gwynne, The Perfect Pass: American Genius and the Reinvention of Football (2016): the story of Hal Mumme, Mike Leach and the Air Raid’s small menu of concepts.
  • Bill Walsh, with Brian Billick and James A. Peterson, Finding the Winning Edge (1998): the West Coast passing game, including its triangle reads, in the coach’s own words.
  • From diagrams to real tracking data. The two animations above are gridiron simulations in NFL Big Data Bowl coordinates. The 2025 Big Data Bowl release (2022 season, weeks 1–9) charts every receiver’s route on every play, so you can find real four-verticals and mesh calls and watch them. The data needs a free Kaggle login, so this code is not run when the book is built.
Show the Python (real Big Data Bowl tracking; not run in the build)
import pandas as pd
from gridiron import bdb, show_animation

plays = pd.read_csv("data/bdb/plays.csv")
player_play = pd.read_csv("data/bdb/player_play.csv")
routes = player_play.dropna(subset=["routeRan"])

# four verticals: at least four receivers on GO routes on the same play
gos = routes[routes["routeRan"] == "GO"].groupby(["gameId", "playId"]).size()
verts = gos[gos >= 4].reset_index()[["gameId", "playId"]]

# mesh-like: two or more crossing routes on the same play, thrown to a crosser
cross = routes[routes["routeRan"] == "CROSS"].groupby(["gameId", "playId"]).size()
mesh = cross[cross >= 2].reset_index()[["gameId", "playId"]]
mesh = mesh.merge(plays[["gameId", "playId", "passResult", "pff_manZone"]], on=["gameId", "playId"])
mesh_vs_man = mesh[mesh["pff_manZone"] == "Man"]

g, pl = verts.iloc[0][["gameId", "playId"]]
trk = bdb.load_tracking("data/bdb/tracking_week_*.csv")
one = bdb.prepare(trk, plays, game_id=g, play_id=pl)
show_animation(one, times=[0.0, 1.2, 2.4, 3.2])   # snap, release, safety's choice, catch

  1. Course calculation from nflverse play-by-play (air_yards) joined to nflverse participation data (time_to_throw, FTN-sourced), 2025 regular season, pass attempts with sacks and spikes excluded. Median time to throw: 2.2 seconds for throws behind the line of scrimmage, 2.3 seconds for 0–9 air yards, 2.8 seconds for 10–19, and 3.1 seconds for 20 or more. EPA per attempt: +0.10 (0–9 yards), +0.39 (10–19), +0.44 (20+); completion rate 72%, 54% and 37%; interception rate 1.6%, 3.2% and 5.0%.↩︎

  2. Chris B. Brown, “One-Trick Pony,” Grantland, January 25, 2013: in Indianapolis under offensive coordinator Tom Moore the play was called “Dig” (“Levels” to most coaches), with the inside receiver on a square-in or dig and the outside receiver on a five-yard in; Manning ran it “ten times a game in various forms,” and 15 or 20 in a big passing game. https://grantland.com/features/how-return-simplicity-peyton-manning-indy-offense-ignited-denver-broncos/↩︎

  3. Course calculation from nflverse play-by-play, 2023–2025 regular seasons: 53,346 pass attempts with charted air yards and direction (pass_location: left, middle, right), sacks and spikes excluded. Throws of 10–14 air yards to the middle averaged +0.52 EPA (n = 2,023) and 15–19 yards +0.60 (n = 1,167), against +0.26 and +0.39–0.40 to the outside thirds at the same depths. The charter’s “middle” is roughly the area between the hash marks; it is a judgment call, not a measured coordinate. Throws behind the line to the middle: n = 877, −0.42 EPA, 60% completed, 2.4% intercepted; joined to FTN charting (nflverse), 35% were screens (is_screen_pass) and 11% throwaways (is_throw_away). Behind the line to the left and right thirds: 81% and 78% completed.↩︎

  4. S. C. Gwynne, The Perfect Pass: American Genius and the Reinvention of Football (Scribner, 2016), on Mumme and Leach and their study of LaVell Edwards’s BYU passing game. Dates: Wikipedia, “Hal Mumme” (UTEP offensive coordinator 1982–85; head coach at Iowa Wesleyan 1989–91, Valdosta State 1992–96 and Kentucky 1997–2000) and “Air raid offense” (Leach head coach at Texas Tech 2000–09, Washington State 2012–19 and Mississippi State 2020–22). Leach, who studied BYU’s passing game as a student there, earned a law degree from Pepperdine before coaching. Pro Football Reference, 1999 NFL Draft (Tim Couch, first overall, Cleveland): https://www.pro-football-reference.com/years/1999/draft.htm .↩︎

  5. Leach, quoted in “ITP Glossary: Mesh Concept,” The Scouting Academy: “the mesh play is a simple crossing pattern, which BYU under LaVell Edwards ran for two decades”: https://scoutingacademy.com/itp-glossary-mesh-concept/ . The Air Raid read order (corner, then the mesh crossers, then the back’s swing) is from Erick Streelman, “Mesh,” Win With The Pass, 2017: “The Progression is Corner, Mesh, Swing”: https://winwiththepass.com/mesh/↩︎

  6. NFL Rulebook, Rule 8, Section 5 (pass interference): pass interference is an act more than one yard beyond the line of scrimmage that significantly hinders an eligible player’s opportunity to catch the ball; for the offense the restrictions run from the snap until the ball is touched, and “blocking downfield by an offensive player prior to the ball being touched” (the pick play) is the first listed example of offensive pass interference. The penalty is 10 yards from the previous spot, with no loss of down (see also Wikipedia, “Pass interference”). https://operations.nfl.com/the-rules/nfl-rulebook/ . Enforcement and how often it is called: Contact Rules and Penalties.↩︎

  7. Wikipedia, “Air Coryell” (Coryell’s offense built on Sid Gillman’s, which made the defense cover the whole field) and “Run and shoot offense” (Mouse Davis); Rodger Sherman, “The Case for the NFL’s Air Raid Revolution,” The Ringer, August 14, 2018, calling four verticals the Air Raid’s “signature play”: https://www.theringer.com/2018/8/14/17685080/air-raid-offense-mike-leach-lincoln-riley . The read-seam that holds against one safety and splits two is described in Chris B. Brown, “One-Trick Pony,” Grantland, 2013.↩︎

  8. Nick Wagoner, “Inside the dynamic plays and schemes built by Shanahan and McVay,” ESPN, October 19, 2018: the yankee route “pairs the outside vertical/post with a deep over route”; the Rams ran it from 11 personnel with jet motion, the 49ers from 12 personnel: https://www.espn.com/blog/san-francisco-49ers/post/_/id/32694/inside-the-dynamic-plays-and-schemes-built-by-shanahan-and-mcvay↩︎

  9. Chris B. Brown, “Speak My Language,” Grantland: https://grantland.com/features/how-terminology-erhardt-perkins-system-helped-maintain-dominance-tom-brady-patriots/ , on the system’s origin under Chuck Fairbanks in New England, its one- and two-word concept names, and Belichick’s turning it into a system of terminology; see also The Essential Smart Football (2012). Staff years from Wikipedia: Ray Perkins was the Patriots’ receivers coach 1974–77 and Ron Erhardt their backfield coach (1973–76) and offensive coordinator (1977–78), so they overlapped in 1974–77; Perkins (head coach) and Erhardt (offensive coordinator) took the system to the Giants in 1982, and Erhardt stayed as Bill Parcells’s coordinator through 1990. Josh McDaniels was New England’s offensive coordinator in 2006–08 and 2012–21, covering 11 of Brady’s 20 seasons there, and returned to the job in 2025.↩︎

  10. Mark Schofield and Dave Archibald, “ITP Glossary: Option Route,” The Scouting Academy, November 3, 2015: the option route is “commonly run from the slot in the Erhardt-Perkins scheme,” and “the New England Patriots have enjoyed terrific success with option routes, getting prolific production out of smaller receivers Troy Brown, Deion Branch, Wes Welker, and Julian Edelman”: https://scoutingacademy.com/itp-glossary-option-route/↩︎

  11. Wikipedia, “Michael Crabtree” (2007: 134 receptions, 1,962 yards, 22 touchdowns in 13 games as a redshirt freshman; Biletnikoff Award 2007 and 2008) and “Graham Harrell” (5,705 passing yards in 2007, the most in the nation); figures also in the Texas Tech Athletics record book.↩︎

  12. Wikipedia, “2008 Texas vs. Texas Tech football game” (No. 6 Texas Tech 39, No. 1 Texas 33, November 1, 2008, Lubbock; Texas led 33–32 after a touchdown with 1:29 left; Tech’s six-play, 62-yard drive ended with Harrell’s pass to Crabtree with one second remaining; Tech’s first win over a No. 1 team): https://en.wikipedia.org/wiki/2008_Texas_vs._Texas_Tech_football_game↩︎

  13. Pro Football Reference, Kliff Kingsbury coaching record (Arizona Cardinals head coach, 2019–2022): https://www.pro-football-reference.com/coaches/KingKl0.htm . Kingsbury was Texas Tech’s quarterback from 1999 to 2002, the last three seasons under Leach, who arrived in 2000 (Wikipedia, “Kliff Kingsbury”). His 2026 role on the Rams staff (assistant head coach) is from the course’s checked 2026 staff list (planning/FACTS-current.md).↩︎

  14. Pro Football Reference, 2018 Los Angeles Rams (13–3; lost Super Bowl LIII to New England, 13–3): https://www.pro-football-reference.com/teams/ram/2018.htm . Goff’s 4,688 yards and 32 touchdowns also match nflverse play-by-play (course calculation).↩︎

  15. Course calculation from nflverse play-by-play, 2018 regular season, pass attempts with sacks and spikes excluded. Throws of 20 or more air yards: league share 12.0%, EPA per attempt +0.37. Los Angeles Rams: 11.3% of attempts (18th of 32), +0.77 EPA (3rd).↩︎

  16. Course calculation from nflverse play-by-play, 2018 week 11 (game_id 2018_11_KC_LA, played November 19, 2018): Rams 54, Chiefs 51; Goff 413 passing yards and 4 touchdowns, Mahomes 478 yards, 6 touchdowns and 3 interceptions. The game was moved from Mexico City to the Los Angeles Memorial Coliseum; see Pro Football Reference: https://www.pro-football-reference.com/boxscores/201811190ram.htm↩︎

  17. Course calculation from nflverse play-by-play, 2018 regular season, same filters: Kansas City threw 15.1% of its attempts 20 or more air yards (5th of 32) at +0.75 EPA per attempt (4th). Mahomes’s 5,097 yards and 50 touchdown passes match Pro Football Reference and nflverse; he was the 2018 AP MVP: https://www.pro-football-reference.com/players/M/MahoPa00.htm↩︎