The Quick Game: Slant-Flat, Stick, Snag, Quick Screens, and Beating Press

Throws that leave the quarterback’s hand in about two seconds: what they are, how he picks one before the snap, and why every offense needs them

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
from matplotlib.lines import Line2D
from matplotlib.patches import Patch

from gridiron import Play, Player, Field, routes as r, style
import gridiron.play as gp

# A firm quick-game throw travels roughly 22-26 yards a second; gridiron's default (18) is an average over
# all throws and makes a five-yard slant look slow. This only changes the setting inside this chapter.
gp.BALL_SPEED = 24.0

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 and read lines on the light field
GREY = style.MUTED
LINE_D, OFF_BALL, GUN = -0.7, -1.5, -5.0


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


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 cover3(nb_side=1, cb_d=7.0, cb_out=1.0, x_w=-15.0, z_w=15.0, extra=None):
    """A 4-2-5 nickel showing Cover 3 against 2x2: corners off with outside leverage, one deep
    safety, and a curl-flat defender walked down over each slot: the nickel ($) on side
    `nb_side` (+1 = offense's right) and the strong safety on the other. extra = {key: (d, w)}."""
    s = nb_side
    out = [D("WDE", "DE", 1.0, -5.4, "E"), D("WDT", "DT", 1.0, -0.8, "T"),
           D("SDT", "DT", 1.0, 2.4, "T"), D("SDE", "DE", 1.0, 5.4, "E"),
           D("MIKE", "MIKE", 4.5, 1.8, "M"), D("WILL", "WILL", 4.5, -2.2, "W"),
           D("LCB", "CB", cb_d, x_w - cb_out, "CB"), D("RCB", "CB", cb_d, z_w + cb_out, "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 = 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 out]


def protect(p, rushers=("WDE", "WDT", "SDT", "SDE"), depth=1.2, rush=1.6):
    """Linemen set, four rushers engage them: enough to say 'pass', no more."""
    for k in ("LT", "LG", "C", "RG", "RT"):
        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)


# ---------------------------------------------------------------- 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=6.2, ax=None, show_ruler=True, **kw):
    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.2, 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"):
    """A dashed path through absolute (d, w) points: the other branch of a read, or a defender's reaction."""
    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=5, 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=5))


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=style.DEFENSE_DARK, 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)
    say(fld, p, d + dy, (w0 + w1) / 2, text, color=color, fontsize=fontsize)


def vbracket(fld, p, w, d0, d1, color=style.INK_2):
    """A vertical dimension line at width w from depth d0 to d1."""
    (x0, y0), (x1, y1) = _P(fld, p, d0, w), _P(fld, p, d1, w)
    fld.ax.annotate("", (float(x1), float(y1)), xytext=(float(x0), float(y0)),
                    arrowprops=dict(arrowstyle="<->", color=color, lw=0.8), zorder=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"),
    "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"),
    "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"),
    "react": Line2D([], [], color=style.DEFENSE, lw=1.4, ls=(0, (3, 2.2)), label="defender's reaction"),
    "other": Line2D([], [], color=READ_GREEN, lw=1.6, ls=(0, (3, 2.2)), label="the other branch"),
    "read": Line2D([], [], color=style.THIRD, lw=1.2, ls=(0, (4, 2)), label="QB's read"),
    "window": Patch(fc=style.THIRD, alpha=0.25, ec=style.THIRD, label="window that opens"),
    "triangle": Patch(fc=style.THIRD, alpha=0.15, ec=style.THIRD, ls=(0, (4, 2)), label="the triangle"),
    "zone": Patch(fc=style.DEFENSE, alpha=0.13, ec=style.DEFENSE, ls=(0, (3, 2)), label="zone defender's area"),
    "taken": Line2D([], [], color=GREY, lw=1.4, ls=(0, (2, 2)), marker="X", ms=7, mfc=style.DEFENSE_DARK,
                    mec="white", label="taken away"),
}


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 len(hs), fontsize=fs,
               frameon=False, handlelength=2.0, columnspacing=1.0)


# ---------------------------------------------------------------- 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):
    """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))
            ax.add_patch(Ellipse((x, y), 0.9, 0.56, 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))
    return fld


def strip(play, times, notes=None, lateral=(-24, 4), window=(-8, 14), ncols=2, rings=(), throw_t=None,
          width_in=6.5):
    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)
        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):
    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)
        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 trail(p, key, target, jab, lag=0.35, off=(-1.1, -0.9), t_open=0.35, t_on=0.9, t_end=4.0):
    """Hand-made keyframes for a defender chasing `target` from behind: he steps to `jab` (a
    reaction to the release), then settles `off` = (down, toward the offense's right) yards from
    where the receiver was `lag` seconds earlier. (gridiron's man() ends on top of the receiver.)
    His depth is floored so he never ends up in the offense's backfield: while the receiver is near the
    line the defender stays hip to hip with him, then the floor relaxes and he trails a step behind."""
    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), (t_open, *jab)]
    for tt in np.round(np.arange(t_open + 0.05, t_end, 0.05), 3):
        f = min(1.0, (tt - t_open) / (t_on - t_open))
        f = f * f * (3 - 2 * f)
        chase = p.position(target, max(0.0, tt - lag)) + np.array(off)
        now_d = float(p.position(target, tt)[0])
        chase[0] = max(chase[0], 0.3, now_d - 0.6 - 0.5 * max(0.0, now_d - 1.5))
        rows.append((tt, *(np.array(jab) * (1 - f) + chase * f)))
    kf[key] = np.array(rows)
    p._compiled = (kf, t0, max(t1, t_end))


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)) / gp.BALL_SPEED) * 10) / 10)
Show the Python (2025 dropbacks with time to throw, from nflverse)
from gridiron.data import nfl, pbp


def dropbacks(season=2025):
    """Regular-season dropbacks (spikes removed) with nflverse participation (time to throw, pressure,
    rushers; FTN-sourced from 2023) joined on."""
    cols = ["season", "season_type", "game_id", "play_id", "pass_attempt", "sack", "qb_scramble", "qb_spike",
            "qb_dropback", "complete_pass", "air_yards", "yards_after_catch", "receiving_yards", "epa", "success"]
    plays = pbp([season], columns=cols)
    plays = plays[(plays["season_type"] == "REG") & (plays["qb_dropback"] == 1) & (plays["qb_spike"] != 1)]
    part = nfl().load_participation([season]).to_pandas().rename(columns={"nflverse_game_id": "game_id"})
    part = part[["game_id", "play_id", "time_to_throw", "was_pressure", "number_of_pass_rushers"]]
    return plays.merge(part, on=["game_id", "play_id"], how="left")


DB25 = dropbacks(2025)
ATT25 = DB25[(DB25["pass_attempt"] == 1) & (DB25["sack"] == 0) & DB25["time_to_throw"].notna()].copy()

Third-and-6, midway through the second quarter. Before the snap the slot defender creeps toward the line, and both linebackers walk up into the gaps beside the center. Six defenders are now standing within a yard or two of the ball, and the broadcast’s analyst says the word you have heard a thousand times: blitz. The ball is snapped. The quarterback takes one big step back from the shotgun, plants, and throws. The outside receiver, who had run three steps upfield and cut sharply inside, catches it eight yards downfield and falls forward for a first down. The play took about two seconds. Nobody touched the quarterback, and the replay doesn’t even bother to show the rush. How did the quarterback know where to throw before the blitz got to him? Why was the receiver open at all, with so many defenders so close? And why do so many drives, even for the best quarterbacks in the league, live on throws that travel five yards? This chapter is about the family of plays that answers all three questions: the quick game.

You’ll be able to…

NoteYou’ll need
  • Routes, drops and stretches from Passing Fundamentals: the route tree (slant, hitch, flat, out, seam, go), the quarterback’s drop, and the two ways to put one defender in a bind, the high-low (vertical) stretch and the horizontal stretch.
  • Protection and hot throws from Pass Protection: five-, six- and seven-man protections, the free rusher, hot routes and sight adjustments, and time to throw.
  • A first look at defense from A First Look at Defense: man and zone, one-high and two-high, the box, and what makes a rush a blitz.
  • Formation words from Spread and Modern Formations: 2x2 and trips, and counting receivers from the sideline in as #1, #2, #3.

What the quick game is

The quick game is the set of pass plays built to get the ball out in roughly 2.5 seconds or less. Three things make a pass play “quick,” and they go together:

  1. A short drop. From under center, the quarterback takes three steps back (a three-step drop); from the shotgun, already five yards deep, he takes one step (some teams teach three quick, short steps that cover the same ground). There’s no time to drift, reset or scan.
  2. Short routes. Most receivers break at five or six yards or less: slants, hitches, flats, short outs. One receiver often runs deep, but mainly to drag a defender out of the way.
  3. A throw timed to the break. The ball leaves as the quarterback’s back foot lands, aimed at the spot where the receiver will be when he makes his cut. If the throw is late, the play is usually dead.

Nothing in the rulebook defines it, and teams draw the line in different places, but analysts usually treat throws out in under 2.5 seconds as “quick.” In 2025 about half of all NFL pass attempts came out that fast; the median attempt left the quarterback’s hand at 2.5 seconds.1 That’s the first thing to understand about the quick game: it is not a gadget or a change-up. It is a large share of everything an NFL offense throws.

Why it exists: three jobs

The quick game survives in every playbook, from the old 49ers to the modern Chiefs, because it does three jobs no other part of the offense does as cheaply.

Job 1: beat the pass rush with the clock. A pass rush is a race. Four rushers against five blockers usually need well over two and a half seconds to get home, if they get there at all; a blitzer nobody blocks can arrive in about two. The quarterback can win that race in two ways: block long enough for the deep routes to develop, or throw before the rush matters. The quick game is the second way. It can’t be sacked if the ball is already gone, and it doesn’t need the offensive line to win, only to hold for a heartbeat. The linemen block it differently, too. A quick throw is released low and early, from a quarterback only a step or two behind them, so they meet the rushers aggressively at the line (the jump set of Pass Protection) to keep the defenders’ hands down and out of the throwing lanes. A defensive lineman who gets his arms up in the gap the slant is thrown through is the quick game’s most common way to lose a ball: the batted pass.

Pass Protection showed one half of the clock’s bargain: the longer the ball stays in the quarterback’s hand, the more often the rush gets to him (under one throw in ten is pressured when it comes out in under two seconds; about half when it is held three and a half seconds or more). Here is the other half, the price of throwing early.

Show the Python
bins = [0, 2.0, 2.5, 3.0, 3.5, 99]
labels = ["< 2.0", "2.0–2.5", "2.5–3.0", "3.0–3.5", "3.5 +"]
a = ATT25.copy()
a["bucket"] = pd.cut(a["time_to_throw"], bins, labels=labels, right=False)
g = a.groupby("bucket", observed=True).agg(n=("epa", "size"), pressured=("was_pressure", "mean"),
                                           air=("air_yards", "mean"), comp=("complete_pass", "mean"))
fig, axes = plt.subplots(1, 2, figsize=(6.4, 3.0))
cols = [style.OFFENSE, style.OFFENSE] + [style.BASELINE] * 3
for ax, col, fmt, top, title in ((axes[0], "air", "{:.1f} yd", 14, "How deep the throw went"),
                                 (axes[1], "comp", "{:.0%}", 1.0, "How often it was caught")):
    vals = g[col].values
    ax.bar(range(len(g)), vals, color=cols, width=0.62, zorder=2)
    for i, v in enumerate(vals):
        ax.text(i, v + top * 0.02, fmt.format(v), ha="center", va="bottom", fontsize=7.6, color=style.INK)
    ax.set_xticks(range(len(g)), list(g.index), fontsize=7.6)
    ax.tick_params(axis="x", length=0, pad=3)
    ax.set_ylim(0, top * 1.12)
    ax.grid(axis="x", visible=False)
    ax.set_title(title, fontsize=9, loc="left")
    ax.set_xlabel("Seconds from snap to throw", fontsize=8)
axes[0].set_yticks([0, 5, 10], ["0", "5 yd", "10 yd"])
axes[1].set_yticks([0, 0.5, 1.0], ["0%", "50%", "100%"])
for ax in axes:
    ax.tick_params(axis="y", labelsize=7.6)
fig.tight_layout(w_pad=1.6)
plt.show()
Figure 22.1: The price of a fast throw. Every bar is a group of 2025 regular-season pass attempts sorted by time from snap to throw. Left: how far past the line of scrimmage the ball traveled, on average. Right: how often it was caught. The two blue bars, throws out in under 2.5 seconds, are the quick game’s territory: they go less than half as far as the slowest throws, and they are caught far more often. Sacks are not throws, so they are left out.

Fast and short come as a package: the quick throws went only three to seven yards past the line of scrimmage on average, against eleven or twelve for the slow ones.

Job 2: keep the offense on schedule. A first down takes ten yards in three plays, and the plays that kill drives are the ones that lose yards or gain none: sacks, incompletions, and the third-and-long that follows them. Coaches call staying ahead of the chains being on schedule: roughly, gaining four or more on first down and half the remaining distance on second. Quick throws are completed far more often (in 2025, about 72% of attempts out in under 2.5 seconds were caught, against 57% of the rest) and almost never end in a sack, so they keep the offense in second-and-5 instead of second-and-10.2 By success rate (the share of plays with positive EPA, meaning plays that left the offense better off than before the snap), quick throws succeeded on about 51% of attempts, slower throws on about 45%.

Job 3: stretch the run game sideways. Bill Walsh, who built the most influential short passing offense in NFL history, started from a team that couldn’t run the ball well enough to control a game, so he used short, high-percentage throws to do the running game’s job; his throws have often been described as extended handoffs.3 A five-yard pass to a receiver near the sideline gets the ball to a fast player in space, away from the big defenders in the middle, and lets him run. In 2025 about 59% of the receiving yards on quick completions came after the catch, against about 38% on slower ones.4 That is the defining fact of the quick game: the throw is short and the run after it does most of the work. It also means that a defense that wants to stop the quick game has to tackle in space, on the edges of the field, where it has the fewest defenders.

ImportantWhy it exists, and what it costs

The quick game trades depth for certainty: a high chance of a small gain, a near-zero chance of a sack and a receiver running before the defense is set, in place of the big gain through the air. The bill comes in three parts. First, the defense can squeeze it. If an offense never threatens deep, defenders creep closer and the short windows shrink; quick game works best when the defense still has to respect the deep ball. Second, it needs yards after the catch: a five-yard throw that is tackled at five yards on third-and-7 is a punt. Third, it gets harder as the field shrinks: near the goal line there is less room to run after the catch. The rest of this chapter is about how good offenses keep the bargain on their side.

A short history: the rest of the Walsh story

You met the start of this story in Passing Fundamentals: Bill Walsh, an assistant on Paul Brown’s Cincinnati Bengals in 1970, building a passing game of short, timed throws around Virgil Carter, a quarterback who was accurate but had no deep arm, and Carter leading the NFL in completion percentage in 1971.5 Two details sharpen it. Carter was never supposed to be the starter: the Bengals’ brilliant young quarterback, Greg Cook, had wrecked his throwing shoulder as a rookie in 1969, so Walsh built the offense around the quarterback he had, not the one he wanted. And Carter, who had studied statistics in college and took an MBA during his playing career, co-wrote one of the first academic papers to estimate the expected points value of field position: the same idea this book uses as its scorecard.6

In San Francisco from 1979, with Joe Montana, the approach became the backbone of three Super Bowl champions (after the 1981, 1984 and 1988 seasons).7 Its signature was the slant to Jerry Rice, thrown on rhythm before the cornerback could react and turned into a long touchdown by Rice’s speed: Rice caught a record 22 touchdown passes in just 12 games in 1987.8 The family of offenses descended from Walsh’s ideas came to be called the West Coast offense, a label with a tangled history that Offensive Systems and Languages untangles.

A rule change helped. In 1978 the NFL barred defenders from contacting receivers more than five yards past the line of scrimmage; until then a defensive back could keep bumping a receiver well down the field.9 Receivers who could no longer be mugged at eight and ten yards made short timing routes far more reliable, and passing numbers rose across the league. Walsh arrived in San Francisco a year later.

Rhythm: the drop and the throw are one motion

Watch a quick throw from the end-zone camera and you’ll notice that the quarterback never seems to decide anything. He catches the snap, takes his steps, and the ball is gone on the last one. That is a rhythm throw: a pass thrown on the last step of the drop, with no hitch or hesitation, so the ball arrives just as the receiver breaks.

The rhythm is the whole point. The quarterback’s drop and the receiver’s route are designed together, step for step: a three-step drop under center, or a one-step drop from the shotgun, is timed to a receiver who breaks at about five or six yards (Passing Fundamentals lays out the matched pairs). When the quarterback’s back foot hits, the receiver is making his cut, and the ball leaves. If the quarterback has to hitch (take an extra reset step) or look somewhere else, the timing is gone and a different, slower play has begun.

You’ll meet three different clocks in this chapter, and they measure different things:

  • About 1 to 1.5 seconds is the design: from the shotgun, a rhythm throw against a receiver who gets a clean release leaves the hand about then.
  • 2.5 seconds is the analysts’ line, the cut-off the data in this chapter uses to call a throw “quick.” It sits well past the design time because real plays lose a beat here and there: a receiver delayed by a jam, a quarterback who waits half a second for a defender to commit, a snap that comes late in his hands. A 2.1-second slant against press is still a quick-game throw; it just shows you the press cost the receiver a step.
  • “One-thousand-two” is the couch rule in the “Watch for it” box at the end: if the ball is gone before you finish saying it, you saw the quick game working as designed.

What does a late quick throw look like? The quarterback’s last step lands and the ball doesn’t come out: he hitches, or his shoulders turn to another receiver, and the slant runner is already two or three steps past his break, drifting into the linebackers. When the throw finally comes, the defender who was a step behind is now at the catch point. That is when you see the batted balls, the contested catches and the interceptions on short routes.

That has a consequence that shapes the whole quick game: the quarterback has almost no time to read the defense after the snap. He gets one look, at one defender, and has to know in advance what that defender’s answer means. Most of the “reading” therefore happens before the snap. To see how, we need one more piece of vocabulary.

Leverage: the pre-snap read

Look at any cornerback before the snap and ask a simple question: is he lined up inside the receiver, outside him, or straight in front of him? That position is his leverage, and it is the most important thing a quarterback looks at before a quick throw.

Show the Python
fig, axes = plt.subplots(1, 2, figsize=(6.5, 3.2))
for ax, shade in ((axes[0], -1.5), (axes[1], 1.5)):
    p = Play([O("X", "WR", LINE_D, -15.0)], [D("LCB", "CB", 6.0, -15.0 + shade, "CB")], los=40, to_go=None)
    p.highlight("LCB")
    if shade < 0:
        p.route("X", r.slant(stem=2.5, length=9))
    else:
        p.route("X", r.quick_out(depth=5.0, width=6.0))
    fld = draw(p, ax=ax, lateral=(-25.0, -4.0), window=(-3.5, 12.5), show_ruler=False)
    if shade < 0:
        taken(fld, p, [(-0.7, -15.0), (5.0, -15.0), (4.7, -20.0)])     # the square quick out he sits on
        say(fld, p, 8.2, -8.9, "slant: open,\nhe has to cross\nX's path to\nget there", color=READ_GREEN,
            fontsize=7, weight="bold")
        say(fld, p, 9.0, -20.4, "out: he's\nalready there", color=style.DEFENSE_DARK, fontsize=7)
        say(fld, p, 2.6, -21.3, "CB outside", color=style.DEFENSE_DARK, fontsize=7)
        panel_title(ax, "Outside leverage: throw inside")
    else:
        taken(fld, p, [(-0.7, -15.0), (2.5, -15.0), (7.0, -10.0)])     # the slant he sits on
        say(fld, p, 9.0, -20.4, "quick out:\nopen, he is\non the wrong\nside", color=READ_GREEN,
            fontsize=7, weight="bold")
        say(fld, p, 9.4, -9.0, "slant: he's\nalready there", color=style.DEFENSE_DARK, fontsize=7)
        say(fld, p, 2.6, -9.0, "CB inside", color=style.DEFENSE_DARK, fontsize=7)
        panel_title(ax, "Inside leverage: throw outside")
legend(fig, ["route", "taken"], y=0.08)
fig.tight_layout(w_pad=1.0, rect=(0, 0.07, 1, 1))
plt.show()
Figure 22.2: Leverage, from the quarterback’s side of the ball. Left: the cornerback lines up a yard and a half outside the receiver, protecting the sideline (outside leverage). Routes toward the sideline run into him; a route that breaks inside, like the slant, starts on the side he isn’t on. Right: the cornerback lines up inside the receiver (inside leverage), protecting the middle; now the quick out is the route that starts on the open side. Throw away from leverage.

Why would a defender give anything away on purpose? Because he can’t take everything away, so he takes away what his teammates can’t help him with. A defender usually lines up on the side where he has no help, and funnels the receiver toward the side where he does. A cornerback with a safety deep in the middle and linebackers underneath often plays outside leverage: his teammates can help on routes inside. A corner whose help is outside plays inside leverage, and that help can be a teammate sitting in the flat in front of him or simply the sideline. Coaches call the sideline the cornerback’s twelfth man, because a receiver near it has nowhere to go but inside or straight up. So leverage is not just one man’s position. It is a clue to the whole coverage behind him.

The receiver’s split, how far from the ball he lines up, feeds the same read from the other side. Split wide, near the sideline, he has little room to break outside, so the corner can shade him inside and let the sideline do the rest. A receiver who reduces his split, lining up closer to the ball, has room both ways, and a defender who sees a reduced split starts expecting an outside-breaking route.

The quarterback’s rule is short: throw away from leverage. A route that breaks toward the side the defender isn’t on gains a step at the break, because the defender has to cross the receiver’s path, or run around him, to get there. That’s the core of the pre-snap read: a look at the defense’s alignment before the snap (leverage, the cushion, meaning how far off the receiver the defender stands, the safeties, the box) that tells the quarterback which side, or which receiver, a play should go to. Many quick-game calls are drawn with a concept on each side of the formation for exactly this reason: the quarterback picks the side whose leverage he likes before the ball is snapped, then reads one defender on that side after it. The coordinator calls the play; the quarterback chooses the side, at the line, inside the rules the offense has practiced all week (many teams pre-set a default, such as “work the side with the extra receiver,” for when both look equal).

Three numbers belong in every pre-snap read of a single receiver:

  • Inside, outside or head-up? Head-up (straight in front of the receiver) is a defender who hasn’t given a side away. It is common in press coverage, and the release section below shows how a receiver deals with it.
  • How deep? A defender seven or eight yards off can’t stop a short throw from being caught; he can only tackle the catch. One pressed up at the line (in press coverage: a yard or so away, ready to jam the receiver at the snap) can disrupt the route itself.
  • Where is his help? A deep safety over the top lets a cornerback sit on short routes. No safety at all means the cornerback has to protect against the deep ball first.
CautionCommon misconception

“The quarterback reads the coverage after the snap.” For deep dropback passes, mostly yes. For the quick game, the important decision is made before the snap and the post-snap read is a single glance at a single defender. When you see a quarterback throw the instant his foot lands, he isn’t guessing; he made the decision ten seconds earlier, from the alignment, and the snap merely confirmed it.

Slant-flat: the high-low

Now the concepts. A pass concept is a set of routes designed to work together, usually to put one defender in a position where he can’t be right. Every quick concept in this chapter is built on one of the two stretches from Passing Fundamentals: a high-low (one receiver above a defender, one below him) or a horizontal stretch (one inside him, one outside him). The defender being stretched is the one the quarterback watches.

The simplest quick concept in football is slant-flat. The outside receiver runs a slant: three hard steps upfield, then a sharp break inside at about 45 degrees. The inside receiver runs to the flat, the area near the sideline just past the line of scrimmage. Between them stands one defender, usually the slot defender or an outside linebacker, whose job in most zone coverages is to guard the short area outside: the flat defender (or, in Cover 3 language, the curl-flat defender). He is the read.

The quarterback’s thought process is one sentence long: “If the flat defender widens, throw the slant; if he sits, throw the flat.” He doesn’t have to diagnose the coverage, find the safeties or work through five receivers. He has to watch one man take one step. That’s why slant-flat is often the first pass concept a young quarterback learns, and why it survives at every level of the sport.

Notice what each route is doing to the defender. The flat route is a low threat: it pulls him toward the sideline and forward. The slant is a high threat: it attacks the space behind him and inside him. It is the same high-low as the curl-flat read in Passing Fundamentals, squeezed into three steps. He can’t be in both places, and he has to commit in about half a second. If he widens, he has opened the door the slant runs through. If he stays put, or sinks into the slant’s path, the inside receiver catches the ball in the flat with no one within five yards, which on most downs is a fine result: a “long handoff” to the edge.

TipWatch for it: what the read defender can do

Coaches describe the one defender’s choice with a handful of verbs. You’ll see them through the rest of this chapter:

He… What he does Throw to
widens runs toward the sideline with the outside or flat route the route inside him or behind him
sits holds his spot the route he isn’t standing on
sinks drops deeper, under the higher route (coaches say he robs it) the low route in front of him
jumps (or drives) runs forward at the short route the route behind him
carries runs up the field with a vertical route the short route he left
bails turns and runs deep at the snap (as Cover 3 cornerbacks do) the short route in front of him

A defense that wants to take slant-flat away coaches its flat defender to sink under the slant and trusts a cornerback squatting in the flat to take the other route, which is why slant-flat struggles against Cover 2.

Show the Python
def slant_flat_play(nb_widens=True):
    off = spread()
    dfn = cover3(nb_side=-1)
    p = Play(off, dfn, los=40, to_go=10)
    protect(p)
    p.dropback(1.5)
    p.route("X", r.slant(stem=3.0, length=10.0))
    p.route("H", r.path((1.0, 1.8), (2.6, 8.5)))
    p.route("Z", r.go(16.0))
    p.route("Y", r.hitch(6.0))
    p.drop("LCB", (14.0, -18.0), speed=5.5)
    p.drop("RCB", (14.0, 18.0), speed=5.5)
    p.drop("FS", (15.0, -1.0))
    p.drop("WILL", (6.8, -3.6), delay=0.15)
    p.drop("MIKE", (6.8, 3.6), delay=0.15)
    p.drop("SS", (6.0, 10.5), delay=0.1)
    if nb_widens:
        p.path("NB", [(-1.6, -3.4), (-2.3, -7.4)], speed=7.0, delay=0.05)
        p.pass_to("X", t=1.0)
    else:
        p.path("NB", [(1.2, -1.2), (2.0, -1.5)], speed=4.0, delay=0.15)
        p.pass_to("H", t=1.0)
    return p


sf = slant_flat_play()
t_catch = catch_time(sf)
_trk = tracking(sf)
_cx = _trk[(_trk["displayName"] == "X") & np.isclose(_trk["time"], t_catch)]
catch_d = float(_cx["x"].iloc[0]) - sf.los
notes = ["The $ is the only defender between\nX and H. He can't cover both.",
         "H bursts to the flat; the $ widens\nwith him, leaving the slant open.",
         "The ball leaves on X's break, behind\nthe $ and in front of the W.",
         f"Caught about {catch_d:.0f} yards deep,\nwith room to run."]
play_movie(sf, [0.0, 0.5, 1.0, t_catch], notes=notes, lateral=(-24.5, 2.5), window=(-8.0, 12.5),
           rings=(("NB", style.THIRD),), throw_t=1.0)
Figure 22.3: Slant-flat against a Cover 3 look, the offense’s left side (simulated, BDB-format tracking). The ringed defender, the nickel back ($), is the quarterback’s only read. He stands between two receivers going in two directions: X’s slant above and inside him, H’s flat below and outside him. Here he widens with H, so the ball goes to X on the slant, into the space between the $ and the Will linebacker (W). Had he stayed inside, the throw would have gone to H in the flat. The ball is out about one second after the snap; faint lines are where each player has been, brown dashes the ball in flight. (Simulated tracking: positions generated by this book’s gridiron library in the same format as the NFL’s public Big Data Bowl tracking data.)

What does slant-flat give up? Depth. The best case is a slant caught at seven or eight yards with room to run; the usual case is a flat route for four or five. It also needs the cornerback to cooperate: against a corner who sits low in the flat (as in Cover 2, where corners squat short), the flat is covered by a second man and the slant must fit into a tighter window. That’s where the other concepts come in.

Stick: the in-out

Stick is one of the most-called quick concepts in the NFL, and you met it in What a Play Really Is as that chapter’s one example play. Here is why it is so popular. Slant-flat stretches the flat defender vertically; stick stretches him horizontally, inside and outside, at a depth that usually reaches the line to gain on second or third and medium.

Three receivers on one side. The outside man (#1) runs off, deep: a go route or a fade that carries the cornerback away. One man runs to about six yards, at or just past the line to gain, and sticks: he settles in the open space and turns to face the quarterback, or, against man coverage, breaks away from his defender. One man runs to the flat, getting to the numbers or wider. The stick sits inside the flat defender; the flat route goes outside him.

Which man runs which route depends on the formation, and you’ll see both versions on Sundays. In the trips version you met in What a Play Really Is (sometimes called Y-stick), the tight end is #3, the innermost receiver, and runs the stick, while #2 runs an arrow to the flat outside him. In the 2x2 version drawn below, the classic NFL drawing, the tight end is #2 and runs the stick, and the running back comes out of the backfield to the flat. The rule underneath is the same: the stick runner settles inside the flat defender, and the flat comes from whoever is outside or under him.

Show the Python
def stick_play(nb_widens):
    off = spread(rb_w=1.5)
    dfn = cover3(nb_side=1)
    p = Play(off, dfn, los=40, to_go=6)
    protect(p)
    p.dropback(2.0)
    p.route("Z", r.go(16.0))
    p.route("Y", r.stick(7.5))
    p.route("RB", r.path((0.5, 4.5), (1.4, 8.5), (5.2, 11.8), (6.8, 13.6)), side=1)
    p.drop("RCB", (13.5, 17.5))
    p.drop("MIKE", (7.2, 2.6), delay=0.15)
    p.highlight("NB")
    p.pass_to("Y" if nb_widens else "RB", t=1.4)
    return p


fig, axes = plt.subplots(1, 2, figsize=(6.5, 3.05))
for ax, widen in zip(axes, (True, False)):
    p = stick_play(widen)
    fld = draw(p, ax=ax, lateral=(-11.5, 25.5), window=(-8.5, 15.5), show_ruler=False)
    if widen:
        branch(fld, p, [(5.0, 7.2), (3.6, 11.0), (3.0, 13.0)], color=style.DEFENSE, lw=1.4)
        opening(fld, p, (5.9, 8.4), (3.4, 4.8))
        say(fld, p, 11.0, 7.0, "stick: open in\nthe space he left", color=READ_GREEN, fontsize=7, weight="bold")
        say(fld, p, 3.8, 21.4, "$ widens", color=style.DEFENSE_DARK, fontsize=7)
        panel_title(ax, "$ widens: throw the stick")
    else:
        branch(fld, p, [(5.0, 7.2), (6.0, 8.2)], color=style.DEFENSE, lw=1.4)
        opening(fld, p, (1.8, 14.4), (3.0, 4.6))
        say(fld, p, 11.0, 7.0, "$ sits inside,\nunder the stick", color=style.DEFENSE_DARK, fontsize=7)
        say(fld, p, -3.2, 16.0, "flat: open", color=READ_GREEN, fontsize=7, weight="bold")
        panel_title(ax, "$ sits: throw the flat")
legend(fig, ["route", "react", "pass", "window"], y=0.09)
fig.tight_layout(w_pad=0.8, rect=(0, 0.08, 1, 1))
plt.show()
Figure 22.4: Stick from 2x2, run to the offense’s right against a Cover 3 look: Z clears out deep, Y (a tight end split into the slot) runs six yards and settles, and the running back runs to the flat. The ringed nickel back ($) is the read, and the two panels are his two choices. Left: he widens toward the back in the flat, so the stick is open in the space he left. Right: he sits, staying inside under the stick, so the back is alone in the flat at the numbers. The Mike linebacker (M) drops to the middle and is too far inside to help on the stick in time; the cornerback goes deep with Z.

The stick route itself is the clever part. It isn’t a hitch with a fixed landmark; it’s a rule. Against zone, the receiver finds the soft spot between the flat defender and the next defender inside (the Mike linebacker here) and sits in it, facing the quarterback, so the ball can be thrown the moment he settles. Against man coverage, there is no soft spot to find, because a defender is following him, so he breaks away from that defender’s leverage, in or out. That’s an option route in miniature, and it is why the quarterback and the stick runner rehearse it endlessly: both of them have to see the same thing.

Stick is everywhere because it does three things at once: it puts the ball at or near the line to gain on the downs where five or six yards matter most; it works against nearly every zone, because nearly all of them have one defender responsible for both the area inside and the area outside at that depth; and the clear-out by #1 keeps a deep threat on the field, so the cornerback can’t come up and sit on everything short.

Snag: the triangle

Slant-flat stretches one defender up and down; stick stretches him side to side. Snag does both at once by putting three receivers in a triangle, so two defenders are stuck guarding three spots. (Some teams call it spot.)

Show the Python
off = spread(rb_w=1.5)
dfn = cover3(nb_side=1)
p = Play(off, dfn, los=40, to_go=10)
protect(p)
p.dropback(2.0)
p.route("Z", r.path((4.0, 0.0), (5.5, -1.5), (5.2, -3.5)))
p.route("Y", r.path((5.5, 0.0), (8.5, 0.0), (14.0, 6.5)))
p.route("RB", r.path((0.5, 5.5), (2.0, 10.0), (2.7, 13.5), (6.0, 16.5)), side=1)
p.drop("RCB", (12.5, 17.5))
p.drop("MIKE", (7.0, 3.0), delay=0.15)
p.highlight("NB")
fld = draw(p, lateral=(-19.0, 26.0), window=(-8.5, 20.5), show_ruler=False)
triangle(fld, p, [(12.5, 15.5), (4.5, 11.5), (1.0, 18.0)])
say(fld, p, 13.4, 13.0, "corner (high)", color=style.OFFENSE_DARK, fontsize=7)
point_to(fld, p, (2.2, 9.6), (4.6, 11.5), "snag", color=style.OFFENSE_DARK, fontsize=7)
say(fld, p, -0.6, 21.2, "flat (low)", color=style.OFFENSE_DARK, fontsize=7)
say(fld, p, 16.0, -10.2, "The reads\n"
    "vs Cover 3 (this look), watch the $:\n"
    "   $ widens?  Snag, inside him.\n"
    "   $ sits on the snag?  Flat.\n"
    "vs Cover 2, watch the cornerback:\n"
    "   CB squats in the flat?  Corner.",
    color=READ_GREEN, fontsize=6.8, ha="center")
legend(fld.fig, ["route", "drop", "triangle"], y=0.07)
plt.show()
Figure 22.5: Snag to the offense’s right against a Cover 3 look. Z, the outside receiver, pushes upfield five yards and settles just inside where he started (the snag). Y, from the slot, runs a corner route over the top, toward the sideline about fifteen yards deep. The running back runs to the flat. Together they make a triangle (shaded): a high-low on the cornerback (corner route over, flat under) and an in-out on the flat defender (snag inside, flat outside). The receivers on the other side are left out. The quarterback’s reads are in the box: against this look, the $.

Snag is read against the coverage the quarterback identified before the snap. Against Cover 3, the look drawn here, the cornerback has a deep third and bails, so the corner route is covered before it starts. The quarterback’s eyes go straight to the flat defender, the $, exactly as in stick: if he widens to the flat, throw the snag inside him; if he sits on the snag, throw the flat. Against Cover 2, the cornerback squats in the flat with a safety deep behind him, and the read moves to the top of the triangle: if the corner squats on the flat route, the corner route goes over his head into the hole in front of the safety; if he sinks under the corner route, the flat is open.

So why call snag instead of stick? Against Cover 3 they are close cousins that read the same man. Snag’s extra is the corner route. Stick’s flat route runs straight into a Cover 2 cornerback squatting in the flat; snag throws over him. A quarterback who isn’t sure before the snap whether he will see Cover 3 or Cover 2 has an answer to both in one call.

Because the triangle combines both kinds of stretch, a single adjustment can’t take it away; the triangle read becomes a whole family in the next chapter, with deeper routes and more time.

Spacing: five windows

Every concept so far stretches one or two defenders. Spacing stretches the whole underneath coverage: four or five receivers settle at about the same depth, five or six yards, at even intervals across the field, each in the gap between two zone defenders. You saw the geometry in Passing Fundamentals’s horizontal stretch (five receivers against Cover 3’s four underneath defenders). As a called concept it adds two things.

First, one route goes underneath. Almost every spacing call sends one receiver, often the back or the innermost slot, on an arrow or flat route to the sideline at two or three yards. Without it, the curl-flat defender can sit at five yards on the hitch and nobody punishes him; with it, he is in a high-low as well. Second, the quarterback works one half of the field. He can’t watch five receivers in a second and a half, so before the snap he picks a side, usually the one where the offense has more receivers than the defense has underneath defenders, and after it he reads one man on that side.

Show the Python
off = spread(rb_w=1.5)
dfn = cover3(nb_side=1, extra={"SS": (7.5, -11.5), "WILL": (7.5, -4.0), "MIKE": (7.5, 3.5), "NB": (7.0, 11.0),
                                 "LCB": (11.5, -17.5), "RCB": (11.5, 17.5), "FS": (14.5, 0.0)})
p = Play(off, dfn, los=40, to_go=10)
protect(p)
p.dropback(2.0)
p.route("X", r.hitch(6.2))
p.route("Z", r.hitch(6.2))
p.route("H", r.path((6.5, 0.0), (7.0, -1.2)))     # settles inside, in the gap between the SS and the W
p.route("Y", r.path((6.5, 0.0), (7.0, -1.8)))     # settles inside, between the M and the $
go_to(p, "RB", (2.4, 15.5), kind="route", via=[(-3.6, 4.0), (0.2, 9.5)])
for k in ("SS", "WILL", "MIKE", "NB"):
    q = p.players[k]
    p.zone((q.d, q.w), (3.5, 6.0))
p.highlight("NB")
fld = draw(p, lateral=(-25.0, 25.0), window=(-8.5, 19.0))
for k in ("X", "H", "Y", "Z"):
    opening(fld, p, tuple(p._cur(k)), (2.4, 2.8))
opening(fld, p, (2.4, 15.5), (2.4, 2.8))
branch(fld, p, [(7.0, 11.0), (5.2, 14.0)], color=style.DEFENSE, lw=1.3)
branch(fld, p, [(7.0, 11.0), (7.4, 8.6)], color=style.DEFENSE, lw=1.3)
say(fld, p, 14.6, 9.6, "$ widens to the flat?\nthrow Z's hitch or Y.\n$ sits on Z? the flat.",
    color=READ_GREEN, fontsize=6.8, weight="bold")
say(fld, p, -5.4, 13.0, "his side: 3 receivers\nvs 2 defenders", color=READ_GREEN, fontsize=7)
say(fld, p, -5.4, -13.0, "the other side: 2 vs 2", color=style.INK_2, fontsize=7)
legend(fld.fig, ["route", "zone", "react", "window"], y=0.07)
plt.show()
Figure 22.6: Spacing against a Cover 3 look, drawn at the moment the quarterback’s back foot lands. The four underneath zone defenders have finished their drops (their areas shaded orange); the corners and the deep safety are on their way to their deep zones. Five receivers sit at five or six yards, each in a gap between two defenders or between a defender and the sideline, and the running back runs an arrow to the right flat underneath. Before the snap the quarterback picked the right half: there, three receivers (Y, Z and the back) face two underneath defenders (M and the ringed $). After the snap he reads the $ alone. On the left, two receivers face two defenders; he doesn’t look there unless the right is taken away.

Spacing is the quick game’s answer to a defense that plays zone and refuses to rush more than four. Its weakness is the mirror image: against man coverage there are no gaps to sit in, so the receivers have to beat their men, and the sitting routes turn into breaks away from leverage, the same rule as the stick route.

Spacing is an old idea, and a natural fit for the Air Raid offenses of Hal Mumme and Mike Leach in college football, which spread receivers across the whole width of the field and made the quick game one of their foundations;10 The College Laboratory tells that story.

Speed out: beating a soft corner to the sideline

Not every quick throw is a two-man read. The speed out is a single receiver’s route, thrown on timing against one particular look: a cornerback playing well off, with inside leverage, who is backing up at the snap. (Names vary by system: some playbooks call this five-yard rounded cut the quick out and save “speed out” for a deeper rounded out off a longer drop. The route here is the short one.)

Show the Python
off = spread()
dfn = cover3(nb_side=1, cb_d=8.0, cb_out=-1.0,
             extra={"SS": (12.0, -9.0), "FS": (12.0, 11.8), "NB": (5.0, 6.8), "MIKE": (4.5, 1.8)})
p = Play(off, dfn, los=40, to_go=10)
protect(p)
p.dropback(1.5)
p.route("Z", [(0, 0), (3.0, 0.0), (4.1, 0.25), (4.8, 0.9), (5.15, 1.8), (5.2, 2.9), (5.0, 4.6), (4.7, 7.0)],
        speed=7.5)                                   # a rounded break: the turn is spread over several steps
p.route("Y", r.seam(14.0))
p.drop("RCB", (11.0, 14.6), delay=0.1)
p.drop("FS", (14.8, 12.6))
p.pass_to("Z", t=0.8)
p.highlight("RCB")
fld = draw(p, lateral=(-6.5, 25.5), window=(-8.5, 17.5))
vbracket(fld, p, 12.0, -0.7, 8.0)
say(fld, p, 5.4, 12.5, "8-yard\ncushion", fontsize=7, ha="left")
point_to(fld, p, (13.0, 19.6), (8.0, 14.0), "CB inside Z\n(inside leverage),\nbacking up", fontsize=7)
point_to(fld, p, (1.2, 22.4), (4.1, 15.9), "rounded\nbreak", color=style.OFFENSE_DARK, fontsize=7)
say(fld, p, -6.4, 11.5, "ball out as the drop\nlands, before the break", color=style.BALL, fontsize=7)
say(fld, p, 15.2, 6.0, "seam holds\nthe safety", color=style.OFFENSE_DARK, fontsize=7)
plt.show()
Figure 22.7: The speed out to the offense’s right against a two-high look. The cornerback lines up eight yards off Z and slightly inside him, and backs up at the snap. Z runs four or five hard steps and rounds his break toward the sideline without slowing down (the curve, not a square corner); the quarterback throws as his one-step drop lands (from under center, on his third step), before Z has even turned, aiming at the sideline. Y’s seam route keeps the safety on that side deep and out of the throw. The pass travels farther than any other quick throw, so it needs an accurate, strong arm.

Why would a cornerback line up inside in the first place? The leverage rule answers it: he lines up where he has no help. Against a two-high look nobody sits in the deep middle, so the routes that hurt him are the post and the dig, breaking inside toward all that open grass, while the sideline protects his outside. (In a quarters-style coverage there’s a second reason: the safety on that side is reading the slot, so the corner is effectively alone on Z, backing up to stay on top of him.)

Why does the speed out beat that? Because the cornerback’s alignment has already made a promise. Lined up inside and eight yards off, he is protecting the middle of the field and the deep ball, and the sideline in front of him is the one area he has conceded. A receiver who runs at him hard and rounds his break toward the sideline, without the throttle-down of a square cut, gets there first. The ball has to beat the corner’s break, which is why the speed out is a timing route: the quarterback lets it go before the receiver turns, to a spot, and trusts him to be there.

It is also the quick throw that most exposes a quarterback’s arm. The ball travels from the middle of the field to the sideline, the longest flight in the quick game, and a throw that floats lets a defender close on it. The dangerous defender is usually not the corner on top but one underneath: a Cover 2 cornerback squatting in the flat, or a Cover 3 curl-flat defender who sinks and widens under the out (the $ in the figure, if he reads it). An interception there has an open field in front of it, which is why coaches call the out a risky throw for a weak arm and why the speed out is a pre-snap call against one look, not a throw into any coverage.

Two more pairs: double slants and hitch-seam

Two last concepts round out the core of the quick game. Each pairs two receivers on one side.

Double slants: both receivers run slants, one shallower and quicker, the other a step deeper, so their paths stack one above the other as they cross in front of the linebackers. (Teams differ on which one goes underneath; many NFL playbooks have the inside man run the deeper slant instead.) Against zone, that is a high-low on the hook defender in the middle. Against man coverage, the version drawn below, it is even better. Two receivers break inside at the same moment, each defender is trying to keep his own man from getting across his face, and the one defender man coverage usually keeps in the middle as a helper (the “hole” or “rat” player, built in Man Coverage) faces the same high-low as a zone hook defender: he can drive on the low slant or sink under the high one, not both. The two paths also cross close to each other, and the defenders have to fight through that traffic; deliberately running into a defender to block him is a foul once it happens more than a yard past the line, the rub route question settled in the next chapter.11

Hitch-seam: the outside receiver runs a hitch (five or six yards, stop, turn back to the quarterback) and the inside receiver runs a seam straight up the field. It is a high-low on the flat defender. Against Cover 3 that defender is usually coached to carry a slot receiver who goes vertical, running up the field with him until the deep defender takes over, which is exactly why the hitch underneath him is usually the throw. The seam is the punishment if he doesn’t carry: it also threatens the deep middle safety, who can’t take a seam on both sides of the field. Offenses keep him honest by running seams from both slots, or by having the quarterback look the safety off (stare at one seam to pull him there) before throwing.

Show the Python
fig, axes = plt.subplots(1, 2, figsize=(6.5, 3.0))
# -- double slants vs press man: the defenders shade outside, then trail
off = [q for q in spread() if q.key in ("X", "H", "LT", "LG", "C")]
dfn = [D("LCB", "CB", 1.2, -15.6, "CB"), D("NB", "NB", 1.4, -9.6, "$"), D("WILL", "WILL", 6.0, -4.6, "W"),
       D("FS", "FS", 13.0, -1.0, "FS")]
p = Play(off, dfn, los=40, to_go=None)
p.route("X", r.slant(stem=3.0, length=10.0))
p.route("H", r.path((1.5, 0.0), (4.0, -3.5), (4.5, -5.5)))
p.highlight("WILL")
fld = draw(p, ax=axes[0], lateral=(-24.0, 1.5), window=(-3.0, 15.0), show_ruler=False)
branch(fld, p, [(1.2, -15.6), (2.8, -15.6), (6.4, -12.6)], color=style.DEFENSE, lw=1.3)
branch(fld, p, [(1.4, -9.6), (1.8, -9.4), (2.4, -6.6)], color=style.DEFENSE, lw=1.3)
say(fld, p, 12.0, -10.0, "X: slant, a step\ndeeper (high)", color=style.OFFENSE_DARK, fontsize=7)
say(fld, p, -1.6, -12.4, "H: quick\nslant (low)", color=style.OFFENSE_DARK, fontsize=7)
say(fld, p, 9.4, -1.8, "W in the\nhole: can't\ntake both", color=style.DEFENSE_DARK, fontsize=7)
say(fld, p, 6.0, -20.0, "CB and $\nchase from\nbehind", color=style.DEFENSE_DARK, fontsize=7)
panel_title(axes[0], "Double slants vs press man")
# -- hitch-seam vs cover 3
off = [q for q in spread() if q.key in ("X", "H", "LT", "LG", "C")]
dfn = [D("LCB", "CB", 7.0, -16.0, "CB"), D("NB", "NB", 5.0, -7.2, "$"), D("WILL", "WILL", 4.5, -2.2, "W"),
       D("FS", "FS", 13.0, 0.0, "FS")]
p = Play(off, dfn, los=40, to_go=None)
p.route("X", r.hitch(5.5))
p.route("H", r.path((10.0, 0.4), (16.0, 0.4)))
p.drop("LCB", (13.0, -17.5))
p.drop("WILL", (6.5, -3.0), delay=0.1)
p.drop("FS", (14.5, -2.0))
p.highlight("NB")
fld = draw(p, ax=axes[1], lateral=(-24.0, 1.5), window=(-3.0, 15.0), show_ruler=False)
branch(fld, p, [(5.0, -7.2), (6.0, -10.5)], color=style.DEFENSE, lw=1.3)
opening(fld, p, (5.2, -15.0), (2.4, 3.2))
opening(fld, p, (11.0, -8.6), (3.4, 3.0))
say(fld, p, 2.0, -20.2, "hitch: open if\nthe $ carries\nthe seam", color=READ_GREEN, fontsize=7, weight="bold")
say(fld, p, 12.4, -13.6, "seam: open if\nthe $ jumps\nthe hitch", color=READ_GREEN, fontsize=7, weight="bold")
panel_title(axes[1], "Hitch-seam vs Cover 3")
legend(fig, ["route", "drop", "react", "window"], y=0.09)
fig.tight_layout(w_pad=1.0, rect=(0, 0.08, 1, 1))
plt.show()
Figure 22.8: Two quick pairs, both on the offense’s left. Left: double slants against press man coverage, with one helper, the Will linebacker (W, ringed), sitting in the middle. H runs a quick, flat slant underneath him; X a deeper slant above him. The cornerback and the nickel back start on the receivers’ outside shoulders, so both slants break away from them and they chase from behind (dashed), and the one helper in the middle can’t take both. Right: hitch-seam against a Cover 3 look. X runs a hitch underneath the bailing cornerback, H a seam straight up the field. The ringed flat defender ($) can stay low on the hitch or carry the seam, not both: if he carries the seam, throw the hitch; if he jumps the hitch, throw the seam behind him.

Seven concepts, and notice how few ideas they really contain. Five of them (slant-flat, stick, snag, double slants and hitch-seam) pick one defender, put two receivers on opposite sides of him (above and below, or inside and outside), and ask the quarterback to watch that one man and throw to the one he doesn’t take. Spacing does the same thing to the whole underneath coverage at once, with the quarterback choosing a half of the field before the snap. The speed out skips the post-snap read entirely: it beats one cornerback’s alignment. In every case the quarterback’s eyes are on one player or one alignment, which is why he can run the quick game at full speed.

Beating press: the receiver’s side

Everything so far assumed the receivers get into their routes on time. The defense’s most direct answer to the quick game is to make sure they don’t. In press coverage, a cornerback lines up right on the receiver, a yard or so away, and tries to jam him at the snap: punch him, reroute him, knock him off his path. Its opposite, off coverage, is a defender who starts five to ten yards away and gives ground (Coverage Fundamentals covers both from the defender’s side).

Press attacks the quick game where it lives: the timing. A slant that should break at three steps breaks at five because the receiver spent two of them fighting through a jam, and the quarterback, whose back foot has already landed, is left holding the ball with a rush closing in. That is why press and the quick game are a matched pair in every game plan: the defense presses to wreck the rhythm, and the offense answers with receivers who can beat press quickly and routes that use the press defender’s own position against him.

The rules frame the fight. Within five yards of the line of scrimmage, an NFL defender may contact a receiver in front of him; beyond five yards, until the ball is thrown, contact that impedes the receiver is illegal contact or defensive holding.12 Either foul costs the defense five yards and gives the offense an automatic first down, which on third-and-8 is a gift. So press is a five-yard fight, and the receiver’s goal is to get through those five yards on his path and on time. Here is how he does it, from the receiver’s side of the ball.

Release: getting off the line

A release is a receiver’s first moves off the line against a pressing defender: footwork to make the defender commit, and hands to keep the defender’s hands off him. The defender wants to get his hands on the receiver’s chest and mirror him; the receiver wants to make him move first, then go the other way.

Show the Python
LAT_R, WIN_R = (-23.0, -7.0), (-3.6, 11.5)


def press_panel(ax, title, x_pts, cb0, cb_pts, labels):
    off = [O("X", "WR", LINE_D, -15.0)]
    dfn = [D("CB", "CB", cb0[0], cb0[1], "CB")]
    p = Play(off, dfn, los=40, to_go=None)
    p.route("X", x_pts, absolute=True)
    fld = draw(p, ax=ax, lateral=LAT_R, window=WIN_R, show_ruler=False)
    branch(fld, p, [cb0] + cb_pts, color=style.DEFENSE, lw=1.4)
    for txt, at, col in labels:
        say(fld, p, at[0], at[1], txt, color=col, fontsize=6.8)
    panel_title(ax, title, fs=8.5)


OD, DD = style.OFFENSE_DARK, style.DEFENSE_DARK
fig, axes = plt.subplots(2, 2, figsize=(6.5, 5.3))
press_panel(axes[0, 0], "1. Inside release",
            [(-0.5, -16.4), (0.2, -15.6), (1.0, -14.4), (2.6, -13.6), (7.5, -9.0)], (1.1, -15.4),
            [(1.0, -16.6), (2.0, -15.6), (5.2, -12.0)],
            [("jab out", (-2.0, -19.0), OD), ("swipe, slip\ninside", (1.0, -11.0), OD), ("slant", (8.9, -11.2), OD),
             ("CB bites on\nthe jab, then\nchases", (6.0, -19.4), DD)])
press_panel(axes[0, 1], "2. Outside release",
            [(-0.5, -13.6), (0.2, -14.4), (1.2, -16.4), (4.0, -17.0), (10.2, -17.2)], (1.1, -14.6),
            [(1.0, -13.4), (2.6, -14.8), (8.6, -15.9)],
            [("sell\ninside", (-2.0, -11.4), OD), ("burst\noutside", (1.4, -20.1), OD), ("go", (10.0, -19.4), OD),
             ("CB bites inside,\nthen trails", (5.4, -11.6), DD)])
press_panel(axes[1, 0], "3. Stem and stack, then break",
            [(1.4, -16.4), (5.0, -16.8), (7.6, -15.2), (8.6, -15.0), (9.4, -11.0)], (1.1, -15.0),
            [(2.6, -15.6), (6.2, -15.1)],
            [("release\noutside", (0.8, -19.2), OD), ("stem at his\noutside shoulder", (4.6, -20.0), OD),
             ("stack: back\non top of him", (8.0, -19.6), OD), ("break", (10.2, -9.6), OD),
             ("CB now\nbehind X", (3.2, -12.4), DD)])
press_panel(axes[1, 1], "4. Double move (slant-and-go)",
            [(2.4, -15.0), (3.6, -13.8), (4.4, -14.4), (7.5, -14.9), (10.8, -15.6)], (5.6, -15.6),
            [(4.6, -12.6)],
            [("fake the\nslant", (2.0, -11.0), OD), ("go", (10.4, -12.4), OD),
             ("CB drives\non the slant", (7.0, -19.4), DD)])
legend(fig, ["route", "react"], y=0.035)
fig.tight_layout(h_pad=1.4, w_pad=1.0, rect=(0, 0.03, 1, 1))
plt.show()
Figure 22.9: Four ways a receiver (X) beats a cornerback (CB), drawn from above on the offense’s left: three against a cornerback pressing him, and one against a cornerback sitting on short routes. Blue is the receiver’s path, orange dashes the cornerback’s reaction. 1. Inside release: a hard jab step outside makes the cornerback step that way too; the receiver swipes the cornerback’s hands down and slips inside, onto a slant. 2. Outside release: he sells inside with a step, the cornerback steps with him, and the receiver bursts outside and up the sideline, with the cornerback trailing on his inside hip. 3. Stem and stack: after the release, the receiver runs his stem straight at the cornerback’s outside shoulder, as if going deep, then moves back directly in front of him (the stack) before breaking. 4. The double move: against a cornerback playing off and waiting for the short route, he fakes a quick slant, the cornerback drives on it, and the receiver turns up the field behind him.

The inside and outside releases are mirror images of one idea: make the defender declare first. Attack one side hard enough that he has to move his feet or reach to stop it, then go to the other side. The hands matter as much as the feet: receivers swipe the defender’s hands down, chop at his wrists or rip an arm through under his, to keep him off the chest. A defender who lands a two-hand jam controls the receiver; one who whiffs has to turn and chase.

Which release a receiver uses is not his whim. On a quick route, the route decides. A three-step slant needs an inside release, a fade or go route an outside one, because there is no time to go one way and come back. (On longer routes there is, which is why panel 3 shows a receiver releasing outside and breaking inside: the stem buys the time.) That’s why the pre-snap read includes the press corner’s leverage: a corner pressing with inside leverage is daring the receiver to win inside, so the quarterback may prefer the fade or a quick out to that side, and the slant to the other.

A corner pressing head-up, square in front of the receiver, hasn’t given a side away; he plans to mirror whichever way the receiver goes. The receiver’s answer is to make him declare anyway: a quick flurry of short, choppy steps in place, or a jab one way and then the other, until the corner’s hips or hands commit, and then a release to the side he left. Against a patient head-up corner that takes an extra beat, so quarterbacks often treat that matchup as a coin flip and look for a better one on the other side.

Stem and stack: setting up the break

Once he is off the line, the receiver still has to get open at the top of the route. The stem is the part of the route between the release and the break, and good receivers treat it as a weapon rather than a run-up. By aiming the stem at one of the defender’s shoulders, the receiver tells him what the route “is”: run at his outside shoulder at full speed and the defender has to worry about the go route and turn his hips to run, which makes a break inside much harder for him to match.

Against a defender trailing in man coverage, the receiver also wants to stack him: get directly in front of him, on his line, so the defender is behind the receiver and can’t see or reach the ball without going through him. A stacked defender can’t undercut a throw, can’t play the receiver’s hands, and has to guess which way the break will go. The best route runners win most of their battles here, in the yard or two between “released” and “broken,” and that’s why coaches say a route is won before the break, not at it.

The break and the catch point

The break is the cut itself, and its quality decides how much separation the receiver has when the ball arrives. A sharp break (sink the hips, plant, explode the other way) creates space against a defender who is mirroring. A rounded break keeps speed, as in the speed out, and works against a defender who is backing up. Either way, on a quick throw the ball is usually already in the air.

Then comes the catch point: the moment the ball and the receiver’s hands meet. If the release, stem and break were good, it’s an easy catch with the defender a step or two away. If not, it’s a contested catch: a catch made with a defender close enough to touch the ball or the receiver’s hands. FTN’s charters flagged about 14% of 2025 targets as contested; only about 42% of those were caught, against about 71% of the rest.13 The quick game is designed to avoid contested catches: a well-run slant-flat or stick throws to the receiver who is not covered. When a quick throw becomes a contested catch, usually against press-man coverage with no open receiver, the offense is relying on a receiver who can win at the catch point, and on a quarterback willing to throw him the ball anyway. Throws placed where only the receiver can reach them, like the back-shoulder throw, are how good pairs make those catches more likely.

The double move: punishing the defender who guesses right

The quick game creates its own counter. A cornerback who has watched film of an offense throwing slant after slant and hitch after hitch learns to sit on those routes, and to jump them the instant the receiver breaks. That is exactly the defender a double move is built for. The receiver fakes a quick route, the quarterback often pump-fakes (starts his throwing motion without letting go) as if throwing it, and when the defender drives on the fake, the receiver turns up the field behind him. The common forms are named for their two parts: slant-and-go (the sluggo), out-and-up, hitch-and-go, the stutter-go (a fake stop), and the stick-nod, in which the stick runner fakes settling and then breaks up the field, the counter to a flat defender who has learned to drive on the stick.

The double move is the quick game’s insurance policy: without it, a defense could squeeze the short routes harder every week; with it, every corner who jumps a slant risks a long touchdown. The cost is time. A double move takes three seconds or more, so offenses tend to call it with an extra blocker, after they have thrown the short version often enough to bait the defender.

Quick screens: now and bubble

The quick game has two close cousins that aren’t really passes in the usual sense. Both are screens, throws to a receiver behind or at the line of scrimmage with teammates blocking in front of him, and both are thrown the instant the ball is snapped. (Slower screens to running backs and tight ends, with linemen releasing to block, are in Play-Action, Bootlegs, and Screens.)

A now screen goes to an outside receiver. He takes a step back (or none) and catches the ball a second after the snap, while the receiver inside him blocks the nearest defender. Teams call versions of it a smoke screen or a quick screen; the names vary by playbook (some use “smoke” for the version with no blocker at all), but the idea is the same.

Show the Python
off = spread()
dfn = cover3(nb_side=-1, cb_d=8.0, cb_out=0.5,
             extra={"NB": (5.0, -7.8), "SS": (12.0, 9.0), "FS": (12.0, -9.0)})
p = Play(off, dfn, los=40, to_go=10)
protect(p)
p.route("X", [(0, 0), (-1.0, 0.3)], speed=3.0)
p.run("X", [(5.5, 1.2)], delay=0.15)
go_to(p, "H", (3.3, -8.6), kind="block")
p.pass_to("X", t=0.35)
p.drop("LCB", (6.5, -15.2), delay=0.25)
p.highlight("LCB")
fld = draw(p, lateral=(-25.0, 7.5), window=(-8.5, 15.5))
vbracket(fld, p, -17.8, -0.7, 8.0)
say(fld, p, 3.6, -21.4, "8-yard\ncushion:\nfree yards", fontsize=7)
say(fld, p, 0.9, -11.2, "H blocks\nthe $", color=style.OFFENSE_DARK, fontsize=7)
say(fld, p, -3.6, -11.0, "ball out at once:\nno drop, no read", color=style.BALL, fontsize=7)
bracket(fld, p, 6.8, -5.6, 5.6, "6 in the box", color=style.DEFENSE_DARK, fontsize=7, dy=1.1)
legend(fld.fig, ["route", "block", "run", "pass"], y=0.07)
plt.show()
Figure 22.10: A now screen against off coverage, the offense’s left side. The cornerback lines up eight yards off X, so X catches the ball about a second after the snap and is already running before the cornerback can close. H blocks the slot defender ($), the only other defender nearby. The box holds six defenders, so outside it on this side the defense has just the cornerback and the $ underneath, with a safety twelve yards deep. The quarterback can decide this before the snap from the cushion alone.

The now screen exists to punish one thing: a defender playing far off the ball. An eight-yard cushion is eight yards the defender has to cover before he can make a tackle, and a receiver who catches the ball at the snap gets to spend those yards running instead. Many offenses attach the now screen to ordinary plays as an extra option (coaches say they tag it on), so that the quarterback, seeing the cornerback backed up eight yards, can throw it as a free five yards instead of running the called play. The ball comes out at once for a rules reason too: the receivers who block for it may not block a defender more than a yard past the line before the pass is thrown, the same one-yard limit that governs rubs.

The bubble screen is the inside version. A slot receiver drifts a step or two back and then out toward the sideline, a path that looks like a bubble when you draw it, and catches the ball already running, while the receivers outside him turn and block.

Show the Python
def trips_right():
    """11 personnel, gun trips right: Y (the tight end, #3) and H (#2) off the ball, Z (#1) on the line."""
    return line5() + [O("QB", "QB", GUN, 0.0, "Q"), O("RB", "RB", GUN, -1.5, "R"), O("X", "WR", LINE_D, -15.0),
                      O("Y", "TE", OFF_BALL, 8.5), O("H", "WR", OFF_BALL, 13.0), O("Z", "WR", LINE_D, 19.0)]


dfn = [D("WDE", "DE", 1.0, -4.6, "E"), D("WDT", "DT", 1.0, -0.8, "T"), D("SDT", "DT", 1.0, 2.4, "T"),
       D("SDE", "DE", 1.0, 5.0, "E"), D("WILL", "WILL", 4.5, -2.2, "W"), D("MIKE", "MIKE", 4.5, 2.6, "M"),
       D("NB", "NB", 5.5, 12.0, "$"), D("RCB", "CB", 7.0, 21.2, "CB"), D("LCB", "CB", 7.0, -16.0, "CB"),
       D("SS", "SS", 12.0, 12.0, "SS"), D("FS", "FS", 12.0, -8.0, "FS")]
p = Play(trips_right(), dfn, los=40, to_go=10)
protect(p)
p.route("H", [(0, 0), (-1.6, 2.0), (-1.8, 4.0)], speed=6.0)
p.run("H", [(1.5, 1.2), (5.8, 1.6), (10.8, 1.8)])
go_to(p, "Z", (5.0, 20.8), kind="block")
go_to(p, "Y", (4.2, 11.4), kind="block")
p.pass_to("H", t=0.45)
p.highlight("H")
fld = draw(p, lateral=(-10.5, 26.0), window=(-8.5, 19.5))
bracket(fld, p, 15.6, 7.0, 22.5, "outside the box: 3 offensive players vs 2 defenders\n(the safety starts 12 yards deep)",
        fontsize=7, dy=1.3)
say(fld, p, -5.6, 12.0, "bubble: a step\nback, then out", color=style.OFFENSE_DARK, fontsize=7)
legend(fld.fig, ["route", "block", "run", "pass"], y=0.07)
plt.show()
Figure 22.11: A bubble screen to the trips side, on the offense’s right. H, the middle of three receivers (#2), bows back and out and catches the ball on the run, already moving toward the sideline. Z (#1) blocks the cornerback and Y, the tight end on the inside (#3), blocks the slot defender ($). Outside the box on that side the offense has three players (two blockers and the ball carrier) against two defenders; the third, the safety (SS), starts twelve yards deep. Before the snap, the quarterback counted those numbers.

Coaches who throw these screens on almost every snap, as many college offenses do and some NFL offenses do, often teach the decision with three words: numbers, leverage, grass.

  • Numbers: count the defenders outside the box on one side and compare them with the offense’s players there. Three against two is a screen. This is the box count seen from the other side: every defender a defense puts in the box to stop the run is one fewer outside it.
  • Leverage: can the blockers get between their defenders and the ball carrier? A defender lined up inside, near the ball, is easy to block from the outside; one lined up wide is not.
  • Grass: how much space is there between the ball carrier and the nearest unblocked defender? A safety twelve yards deep is twelve yards of grass.

That is why quick screens are often thought of as part of the run game, and why they are so often packaged with runs. If the defense puts a seventh or eighth man in the box to stop the run, someone outside the box has left, and the screen to that side gets better. When the quarterback decides between the run and the screen after the snap, by reading a defender, the play becomes a run-pass option, and the bubble attached to a run is one of the most common RPOs in football (bubble RPO).

One rule detail makes the bubble unusual. Because it is often thrown sideways or slightly backward, a bubble that is thrown behind the receiver is a backward pass, and a backward pass that hits the ground is a live ball, not an incompletion: either team can recover it.14 That’s why you sometimes see a defender scoop up a dropped bubble and run the other way, and why offenses want the throw to be clearly forward or clearly caught.

The answer to press and blitz

Now put the chapter together. The two most aggressive things a defense can do against the pass are to blitz (send more rushers than the offense can comfortably block) and to press (jam receivers to destroy the timing). Both are bets that the quarterback won’t get the ball out in time. The quick game is the offense taking that bet.

The arithmetic of a blitz explains why. A five-man pressure sends five rushers. Keep six men in to block (the five linemen and the back) and the offense can pick up all five with a man to spare. Keep only five and it can still block five rushers on paper, but only if the protection is aimed at the right five. Protections are set before the snap by counting the defenders who might rush (Pass Protection’s slide and Mike identification). If the line slides toward two linebackers and one of them drops into coverage while a defender from the other edge comes, one blocker is left with nobody and one rusher has no blocker at all: a free rusher. Send six against five and somebody is free no matter what. Either way the protection’s rule is that the free man belongs to the quarterback. He is “hot”: the ball has to come out before he arrives, on a hot route or a sight adjustment aimed at the area the blitzer just left.

Behind the blitz, the coverage is thin. It is often man coverage, pressed, with one safety deep or none, or a fire zone (three deep defenders and three underneath, built in Zone Blitz and Simulated Pressure). In either case the blitzer’s old spot is empty. A quick throw attacks every part of that: it beats the free rusher to the quarterback, and it goes to a receiver who is alone in coverage or running into the hole the blitzer left.

Show the Python
def pressure_play():
    off = spread(rb_w=1.5)
    dfn = [D("WDE", "DE", 1.0, -5.4, "E"), D("WDT", "DT", 1.0, -0.8, "T"), D("SDT", "DT", 1.0, 2.4, "T"),
           D("SDE", "DE", 1.0, 5.4, "E"), D("WILL", "WILL", 4.5, -4.0, "W"), D("MIKE", "MIKE", 2.2, 0.6, "M"),
           D("NB", "NB", 4.5, -10.5, "$"), D("LCB", "CB", 1.2, -15.6, "CB"), D("RCB", "CB", 1.2, 15.6, "CB"),
           D("SS", "SS", 6.0, 8.6, "SS"), D("FS", "FS", 13.0, 0.0, "FS")]
    p = Play(off, dfn, los=40, to_go=10)
    # a five-man slide right: LT and LG take the two linemen in front of them, C/RG/RT step to their right
    for k, (dd, ww) in (("LT", (-1.3, -1.4)), ("LG", (-1.0, 0.2)), ("C", (-1.0, 1.3)), ("RG", (-1.0, 1.0)),
                        ("RT", (-1.2, 1.2))):
        p.block(k, pts=[(dd, ww)], speed=4.0)
        p.block(k, pts=[(-0.4, 0.0)], speed=0.6)
    for k in ("WDE", "WDT", "SDT", "SDE"):           # the four linemen rush and engage them
        p.rush(k, pts=[(-1.6, 0.0)], speed=4.0)
        p.rush(k, pts=[(-0.4, 0.0)], speed=0.6)
    p.dropback(2.0, speed=4.0)
    go_to(p, "NB", (-6.4, -1.4), kind="rush", speed=6.3, delay=0.2, via=[(1.0, -7.2)])
    p.route("RB", r.path((0.5, 4.5), (1.4, 8.5), (5.0, 10.5), (6.0, 11.5)), side=1)
    p.man("MIKE", "RB")
    # X: a jab outside against the press corner, an inside release through his hands, then the stem and slant
    p.route("X", [(0, 0), (0.2, 0.8)], speed=2.4)
    p.route("X", [(0, 0), (2.2, -1.6)], speed=4.4)                 # hand-fight through the jam
    p.route("X", [(0, 0), (3.4, -0.2)], speed=4.4)                 # re-accelerate up the stem
    p.route("X", [(0, 0), (4.2, -5.8), (5.4, -8.0)], speed=6.6)    # the break and the slant
    p.route("H", r.seam(18.0))
    p.man("WILL", "H")
    p.route("Y", r.hitch(6.0))
    p.man("SS", "Y")
    p.route("Z", r.go(16.0))
    p.man("RCB", "Z")
    p.drop("FS", (14.0, 0.0))
    p.pass_to("X", t=2.1)
    trail(p, "LCB", "X", jab=(1.3, -16.2), off=(-0.9, -0.9), t_on=1.1)  # bites on the jab, then chases from behind
    return p


pz = pressure_play()
t_c = catch_time(pz)
_tr = tracking(pz)
_f = _tr[np.isclose(_tr["time"], 2.1)]
gap = float(np.hypot(*(_f.loc[_f.displayName == "NB", ["x", "y"]].values[0]
                       - _f.loc[_f.displayName == "QB", ["x", "y"]].values[0])))
notes = ["The line slides right, toward the M.\nThe $ starts in the slot.",
         "M drops to cover the back, so C has\nnobody. The $ is free: he's hot.",
         f"Ball out at 2.1 s as X comes out of\nhis break; the $ is still {gap:.0f} yd away.",
         "Caught in front of the trailing CB,\nin the space the $ left."]
play_movie(pz, [0.0, 1.0, 2.1, t_c], notes=notes, lateral=(-24.0, 8.0), window=(-9.0, 13.5),
           rings=(("NB", style.DEFENSE_DARK), ("X", style.THIRD)), throw_t=2.1)
Figure 22.12: Quick game against a five-man pressure (simulated tracking in the Big Data Bowl format). The defense shows press-man coverage with one deep safety (Cover 1), with the Mike linebacker (M) walked up over the center as a possible sixth rusher. The running back releases into a route, so the offense has five blockers, and the line slides right toward the Mike: the left tackle takes the end in front of him, the left guard the tackle, and the center, right guard and right tackle step right. At the snap the Mike drops out to cover the back, so the center has nobody, and the slot defender ($, ringed in orange) blitzes off the offense’s left edge, where nobody is left to block him. He is the quarterback’s man: hot. X (ringed in green) beats the press cornerback with an inside release, which costs him a beat, and breaks in on a slant into the area the blitzer left. The ball is out 2.1 seconds after the snap, a beat before the blitzer gets there.

Three details in that play are worth slowing down for. First, the quarterback knew before the snap where the ball was likely to go. Pressed corners with a single safety deep is a classic pressure look, and the slot defender creeping toward the line told him which area might empty. His pre-snap plan was “if the $ comes, the ball goes to X, inside, right now.” Second, he confirmed it with one look after the snap. The $ might have been bluffing, showing a blitz and then backing out into coverage; the moment he crossed the line of scrimmage, the plan was on. Had he backed out, the quarterback would simply have run the play’s normal read. Third, the slant was the right route for the press cornerback’s leverage: he lined up on X’s outside, so an inside release was the release X could win. That release cost X a beat (against off coverage the same slant would come out around 1.2 to 1.5 seconds), which is why this ball left at 2.1: still on time for X, still ahead of the blitz.

Show the Python
d = DB25[DB25["number_of_pass_rushers"].notna() & DB25["epa"].notna()].copy()
quick = (d["pass_attempt"] == 1) & (d["sack"] == 0) & (d["time_to_throw"] < 2.5)
held = (d["sack"] == 1) | (d["qb_scramble"] == 1) | ((d["pass_attempt"] == 1) & (d["time_to_throw"] >= 2.5))
d["outcome"] = np.select([quick, held], ["quick", "held"], default="unknown")
d = d[d["outcome"] != "unknown"]
d["rush"] = np.where(d["number_of_pass_rushers"] >= 5, "Five or more rushers", "Four or fewer rushers")
g = d.groupby(["rush", "outcome"])["epa"].agg(["mean", "std", "size"])
g["se"] = g["std"] / np.sqrt(g["size"])
fig, ax = plt.subplots(figsize=(6.4, 2.9))
rows = ["Four or fewer rushers", "Five or more rushers"]
for i, rush in enumerate(rows):
    for j, (oc, col, lab) in enumerate((("quick", style.OFFENSE, "ball out in under 2.5 s"),
                                         ("held", style.MUTED, "held 2.5 s or longer, or sacked / scrambled"))):
        m, se, n = g.loc[(rush, oc), ["mean", "se", "size"]]
        y = i + (0.19 if j == 0 else -0.19)
        ax.barh(y, m, height=0.34, color=col, label=lab if i == 0 else None, zorder=2)
        ax.errorbar(m, y, xerr=1.96 * se, color=style.INK_2, lw=0.9, capsize=2, zorder=3)
        x_txt = max(m, 0) + 1.96 * se + 0.012
        ax.text(x_txt, y, f"{m:+.2f}  (n = {int(n):,})", va="center", fontsize=7.5, color=style.INK)
ax.axvline(0, color=style.BASELINE, lw=1.0, zorder=1)
ax.set_yticks([0, 1], rows, fontsize=8.5)
ax.set_xlim(-0.2, 0.42)
ax.set_xlabel("Expected points added per dropback", fontsize=8.5)
ax.grid(axis="y", visible=False)
ax.legend(loc="upper center", bbox_to_anchor=(0.42, -0.25), fontsize=7.6, ncol=2)
ax.set_title("Against the blitz, the ball that comes out fast is worth the most (2025)", fontsize=9.5, loc="left")
fig.tight_layout()
plt.show()
Figure 22.13: What the quick game is worth against the blitz. Every 2025 regular-season dropback with a charted number of pass rushers, sorted by the rush (four or fewer, or five or more) and by the outcome: a pass out in under 2.5 seconds (blue), or anything slower, sacks and scrambles included (grey). The bars are expected points added (EPA) per dropback; the thin lines are 95% confidence intervals. Against five or more rushers, quick throws gained about a third of a point more per dropback than everything else. A caution on reading it: quarterbacks hold the ball longer partly because nobody is open, so this comparison flatters quick throws; it shows the size of the stakes, not a controlled experiment.

The chart puts a number on the trade.15 Against a standard four-man rush, a quick throw was worth about an eighth of a point more than a slower outcome. Against five or more rushers, the gap roughly doubles: quick throws gained about +0.23 expected points per dropback, while everything slower, with the sacks that a blitz produces, averaged about -0.10. A blitz is a coin flip the defense is offering, and a quarterback who gets the ball out before the extra rusher arrives wins the flip far more often than he loses it.

So why do defenses blitz at all, on about one dropback in four? Because pooled over every outcome, the bet is roughly even. In 2025, dropbacks against five or more rushers averaged about +0.05 expected points for the offense, against +0.04 for four or fewer: a hair worse for the defense, within the noise, but with more sacks (about 7.8% of dropbacks against 6.0%). And blitzes come more often on third down (about 30% of dropbacks, against 25% on first and second), where forcing a short throw that falls short of the line to gain is a win in itself. The blitz is a variance play: it produces sacks and hurried incompletions, and it gives up the quick completion into the space it vacated. That is why blitz-happy defenses spend so much effort disguising when and where the pressure will come from (Blitz and Man Pressure does the math from the defense’s side; Zone Blitz and Simulated Pressure the disguises).

NoteMadden vs. real life

In a video game, the blitz is a button you press to make the other player panic. In the NFL, a quarterback who sees six defenders at the line is usually not panicking; his offense has spent the week on what to do when that happens. Every quick concept is installed with its blitz answer: which receiver becomes hot (the ball goes to him at once, often on a shortened version of his route) if a particular defender comes, which routes become sight adjustments, and which rusher, if any, the protection call leaves unblocked. The real contest is whether the defense can hide the blitz until it’s too late to adjust, or show one and not bring it. That’s why the pre-snap dance of defenders walking up and backing out, covered in The Pre-Snap Phase, matters so much.

Film room: three offenses that lived on it

The quick game is in every playbook, but a few offenses built their identity on it. Each of the three below shows a different reason to.

NoteFilm room: New Orleans Saints 2017–2019

The quick game as accuracy. Late in Drew Brees’s career, the engine of Sean Payton’s offense (Payton was head coach 2006–2021, Brees the quarterback 2006–2020) was the short, on-time throw. Brees broke the NFL’s single-season completion-percentage record in 2017 (72.0%) and broke it again in 2018 (74.4%).16 In 2019, among quarterbacks with 300 or more attempts, he had the second-fastest average time to throw and one of the shortest average throws (about 6.5 yards), and Michael Thomas set the single-season receptions record with 149, about seven in ten of them on throws that traveled less than ten yards in the air.17

What to notice on the tape: the ball leaving on Brees’s last step, again and again, and Thomas getting inside the cornerback on the slant, then shielding the ball with his body at the catch point.

NoteFilm room: New England Patriots 2007–2012

The quick game as an option tree. The Tom Brady–era Patriots ran it through a small, quick slot receiver. Wes Welker caught 112 passes in 2007, 123 in 2009 and 122 in 2011, leading the league three times in five years,18 most of them within a few yards of the line, many on option routes in which he read his defender’s leverage and broke away from it, the same rule the stick route uses. Brady’s clock stayed fast long after: in 2022, at 45, he had the fastest average time to throw of any regular starter.19

What to notice on the tape: Welker inside, a defender over him trying to guess his break, and Brady throwing as Welker turned his head, before the break had finished.

NoteFilm room: Kansas City Chiefs 2022–2024

The quick game as an adaptation. In 2018, Patrick Mahomes’s first season as the starter, the Chiefs threw deep more than almost anyone, with Tyreek Hill as the threat. Defenses adjusted first. Their throws were already getting shorter in 2019, 2020 and 2021, with Hill still on the roster, as opponents played two deep safeties to take away the big play: by 2021, Kansas City saw a two-high shell on about 46% of its dropbacks, third-most in the league.20 Then Hill was traded to Miami in March 2022,21 and over the next three seasons Andy Reid rebuilt the offense around shorter throws to Travis Kelce and a rotating cast of receivers. By 2023 and 2024 the Chiefs’ average throw was the second-shortest in the league (the chart after this box shows the whole slide). They won the Super Bowl after the 2022 and 2023 seasons anyway.

What to notice on the tape: Kelce, lined up in the slot or as the innermost receiver of trips, running a stick or an option route at five to eight yards and settling between two zone defenders, with Mahomes throwing as he turns. Nearly three of every four of Kelce’s targets in 2022 traveled less than ten yards in the air.22 For one clip, find Super Bowl LVII (February 12, 2023, against Philadelphia): in the fourth quarter, on third-and-goal from the 4, Skyy Moore went in motion, reversed and ran to the flat, and Mahomes threw it to him for a touchdown 1.7 seconds after the snap, against a defense charted as Cover 0 with seven rushers. The Chiefs had scored on the same motion play, run to the other side to Kadarius Toney, less than three minutes of game clock earlier.23 That’s the quick game at its purest: everybody coming, nobody deep, and the ball gone before it matters.

The 2025 point on the chart below matters too: with a different set of receivers, the Chiefs’ depth went back to about the league median. The quick game here was never a fixed identity; it was a great offense taking what the defenses of those years left underneath.

Show the Python
cols = ["season", "season_type", "posteam", "pass_attempt", "sack", "qb_spike", "air_yards"]
pl = pbp(list(range(2016, 2026)), columns=cols)
att = pl[(pl["season_type"] == "REG") & (pl["pass_attempt"] == 1) & (pl["sack"] == 0) & (pl["qb_spike"] != 1)
         & pl["air_yards"].notna()]
team = att.groupby(["season", "posteam"])["air_yards"].mean().unstack()
kc, med = team["KC"], team.median(axis=1)
rank = team.rank(axis=1, ascending=False)["KC"]
fig, ax = plt.subplots(figsize=(6.4, 3.2))
ax.axvspan(2021.5, 2024.5, color=style.OFFENSE_TINT, alpha=0.55, lw=0, zorder=0)
ax.fill_between(team.index, team.quantile(0.1, axis=1), team.quantile(0.9, axis=1), color=style.GRID, alpha=0.9,
                lw=0, label="middle 80% of teams", zorder=1)
ax.plot(med.index, med.values, color=style.MUTED, lw=1.6, label="median team", zorder=2)
ax.plot(kc.index, kc.values, color=style.OFFENSE, lw=2.2, marker="o", ms=5, label="Kansas City", zorder=3)
for yr, dy in ((2018, 10), (2021, -22), (2023, -22), (2024, -22)):
    ax.annotate(f"{kc[yr]:.1f} yd\n(rank {int(rank[yr])})", (yr, kc[yr]), xytext=(0, dy), textcoords="offset points",
                ha="center", fontsize=7.3, color=style.INK, linespacing=1.1)
ax.text(2023, 9.95, "after the Hill trade", ha="center", fontsize=7.5, color=style.OFFENSE_DARK)
ax.set_xticks(range(2016, 2026))
ax.tick_params(labelsize=8)
ax.set_ylabel("Average depth of target (yd)", fontsize=8.5)
ax.set_ylim(5.0, 10.4)
ax.legend(loc="upper center", bbox_to_anchor=(0.5, -0.1), fontsize=7.5, ncol=3)
ax.set_title("The Chiefs' throws got shorter (rank 1 = deepest of 32)", fontsize=9.5, loc="left")
fig.tight_layout()
plt.show()
Figure 22.14: The Chiefs’ average depth of target (how far past the line of scrimmage the ball was thrown, on average) by season, 2016–2025, against the league: the grey band holds the middle 80% of teams and the grey line is the median team. Kansas City went from one of the deepest-throwing teams in 2018 (fifth of 32) to second-shallowest in 2023 and 2024. The slide began in 2019, with Tyreek Hill still on the roster, and deepened after his trade in March 2022 (shaded seasons), with Travis Kelce at the center of the short game; in 2025 the Chiefs were back at about the median. Regular season, sacks and spikes excluded; nflverse play-by-play.

Watch for it on Sunday

TipWatch for it: one-two-throw

Before the snap

  • The corners. Off or pressed? Inside or outside the receiver? A corner seven yards off with outside leverage is inviting a slant or a now screen; a pressed corner is daring the receiver to beat him off the line.
  • The slot defender. Where is the man between the slot receiver and the box? He is the read on almost every quick concept to that side. If he walks up toward the line, think blitz, and think quick throw.
  • Count outside the box. Three receivers against two defenders on one side, with the safety far away, is a screen waiting to happen.

At the snap: the stopwatch. Count “one-thousand-one, one-thousand-two” from the snap. If the ball is gone before you finish “two,” you just watched the quick game working as designed. Count a dozen dropbacks in a game and you’ll find that roughly half of them are out before “two-and-a-half.”

At the snap: watch the linemen. Three plays come out in about a second, and the linemen tell them apart. On a quick throw they set to pass-block, short and aggressive, and stay at the line. On a run-pass option they fire forward as if blocking a run. On a now or bubble screen the receivers near the ball turn and block.

After the throw

  • On the replay, find the one defender between the two receivers on the side the ball went. Did he widen, sink or sit? The ball went to the receiver he didn’t take.
  • When the defense blitzes, watch where the ball goes. Usually it goes quickly to the area the blitzer came from.

For your prediction log (How to Watch a Broadcast): before each third-and-short-to-medium, write quick or not quick, and the side you expect, based on the corners’ leverage. Then time the throw.

Predict the play

TipPredict the play 1: pick a side
Show the Python
off = spread()
dfn = cover3(nb_side=-1, extra={"RCB": (1.3, 14.2), "SS": (3.5, 12.0), "NB": (5.0, -7.0)})
p = Play(off, dfn, los=45, to_go=6)
protect(p)
p.dropback(1.5)
for k in ("X", "Z"):
    p.route(k, r.slant(stem=3.0, length=9.0))
for k in ("H", "Y"):
    p.route(k, r.path((1.0, 1.8), (3.0, 10.0)))
fld = draw(p, lateral=(-25.0, 25.0), window=(-8.5, 15.5))
plt.show()
Figure 22.15: Second-and-6. The offense has called slant-flat to both sides (outside receiver on a slant, slot receiver to the flat) and left the choice of side to the quarterback.

Which side should the quarterback pick, and which defender does he watch after the snap?

The left side, reading the slot defender ($). Run through the pre-snap read on each side.

On the right, both routes are in trouble before the snap. The cornerback is pressed with inside leverage, sitting right where Z’s slant wants to go, so Z has to beat a jam and get across the cornerback’s face; and the safety has already widened to the flat, standing where Y’s flat route ends. One defender is in each receiver’s path.

On the left, the cornerback is seven yards off and outside X, which concedes the slant (away from leverage), and the slot defender is inside H, the one man who has to choose between the two routes. That’s the slant-flat picture from earlier in the chapter. After the snap the quarterback watches only the $: if he widens with H, throw the slant; if he sits, throw the flat.

Doesn’t the right side have its own answer? A corner pressing with inside leverage invites an outside release and the fade, and Z might win it. But the fade isn’t in this call, and even when it is, it’s a lower-percentage throw against a corner who is right on top of the receiver. On the left, the play works exactly as designed, with two receivers against one defender and an easy read. The quarterback takes the side where the concept he called is already winning.

This is why offenses call a quick concept to both sides. The defense has to give something away somewhere, and the quarterback’s pre-snap job is to find where.

TipPredict the play 2: six at the line
Show the Python
off = spread()
dfn = [D("WDE", "DE", 1.0, -5.4, "E"), D("WDT", "DT", 1.0, -0.8, "T"), D("SDT", "DT", 1.0, 2.4, "T"),
       D("SDE", "DE", 1.0, 5.4, "E"), D("WILL", "WILL", 1.9, -2.3, "W"), D("MIKE", "MIKE", 1.9, 0.8, "M"),
       D("NB", "NB", 3.0, -8.6, "$"), D("SS", "SS", 3.0, 8.6, "SS"), D("FS", "FS", 6.0, 0.6, "FS"),
       D("LCB", "CB", 1.3, -13.8, "CB"), D("RCB", "CB", 1.3, 13.8, "CB")]
p = Play(off, dfn, los=50, to_go=5)
fld = draw(p, lateral=(-25.0, 25.0), window=(-8.5, 12.5))
plt.show()
Figure 22.16: Third-and-5, just before the snap. The offense has five linemen, and a running back who can stay in to block or go out for a pass.

Count the threat. What kind of throw does the offense want, how fast, and where?

A hot throw, out in about a second and a half, and away from the cornerbacks’ leverage. Count first. Nobody is deeper than about six yards: this is a “zero” look, with no deep safety at all (Cover 0). Zero lets the defense put a man on every receiver and send everyone else. With five receivers out, that is six rushers against five blockers. If the back stays in to block, his defender can come too: seven rushers against six blockers. Either way one rusher is free, and nobody can hold the ball. That is the Cover 0 blitz bet: the defense is wagering that the ball can’t come out in time.

Now look at the cornerbacks. Both are pressed and both are a yard inside their receivers. That is the leverage rule at work: with no safety anywhere, the corner has no help inside, so he protects the middle and uses the sideline as his help. The slant, the quick game’s favorite throw, is the route he is sitting on. So the ball goes away from his leverage, outside. Two answers fit:

  • The outside receivers’ fade or back-shoulder throw. With no safety behind the corner, a receiver who wins outside off the line is one catch from a touchdown, and the quarterback can throw it as soon as the receiver clears the jam. It’s not a high-percentage throw (a pressed corner is right on the receiver), which is why it isn’t the answer every time.
  • The hot route into the space a blitzer leaves. Some of the defenders crowding the line will cover and some will rush. The receiver whose nearest defender rushes is hot: he breaks into the space that defender just left, and the ball goes there at once.

Which side? The side the protection call says the free rusher is coming from, because that is where the hot receiver is and where the quarterback has the least time. A now screen is a poor choice: the cornerback is standing a yard away, so there is no cushion to run into.

For a real one, see the Super Bowl LVII touchdown to Skyy Moore in the Chiefs film room above: seven rushers, Cover 0, ball out in 1.7 seconds to the flat.

The defense might be bluffing, of course: show zero, bring four, drop two into the throwing lanes. That is exactly why the throw is a read of where the extra defenders went, not just a reflex (Zone Blitz and Simulated Pressure).

TipPredict the play 3: count them
Show the Python
dfn = [D("WDE", "DE", 1.0, -4.6, "E"), D("WDT", "DT", 1.0, -0.8, "T"), D("SDT", "DT", 1.0, 2.4, "T"),
       D("SDE", "DE", 1.0, 5.0, "E"), D("WILL", "WILL", 4.5, -2.6, "W"), D("MIKE", "MIKE", 4.5, 1.0, "M"),
       D("SS", "SS", 5.0, 4.4, "SS"), D("NB", "NB", 7.0, 12.4, "$"), D("RCB", "CB", 7.5, 19.8, "CB"),
       D("LCB", "CB", 7.0, -16.0, "CB"), D("FS", "FS", 14.0, -1.0, "FS")]
p = Play(trips_right(), dfn, los=30, to_go=10)
fld = draw(p, lateral=(-25.0, 25.0), window=(-8.5, 17.5))
plt.show()
Figure 22.17: First-and-10. The offense lines up in trips to its right.

The defense has loaded the box to stop the run. What quick throw does that invite, and why?

A quick screen to the trips side: a bubble to H, or a now screen to Z. Start by counting. The box (roughly the area between the offensive tackles, out to about five yards deep) holds seven defenders: four linemen, two linebackers and a safety. Outside it on the right there are only the cornerback and the $. Then run the three questions:

  • Numbers: on the right, outside the box, the offense has three players (Y, H and Z) and the defense two. The safety who would usually be the third defender out there is standing beside the linebackers, in the box. That’s the trade the defense made: an extra run defender inside, one fewer pass defender outside.
  • Leverage: the cornerback and the $ are both off and roughly over their receivers, so the blockers start in the right place: Z can turn the cornerback out toward the sideline, and Y, starting inside the $, can block him from the inside while H runs between them.
  • Grass: both are about seven yards off, and the nearest help, the deep safety, is fourteen yards away in the middle of the field. A bubble caught at the snap is five yards gained before anyone can touch it.

A run into the middle would face seven defenders with five linemen to block them (six if a receiver comes in to help): the defense has the numbers inside. The quick screen is how the offense “runs” outside the box instead, and it’s why so many offenses attach a bubble to their running plays (Run-Pass Options).

Takeaways

  • The quick game is the passing game’s fast lane: a short drop, short routes and a rhythm throw, out in about 2.5 seconds or less. It is roughly half of what NFL offenses throw.
  • It exists for three jobs: beat the rush with the clock, keep the offense on schedule with high-percentage throws, and stretch the run game sideways by getting the ball to fast players in space. It trades depth for certainty and needs yards after the catch and a deep threat to keep the bargain.
  • Most quick concepts put two receivers on opposite sides of one defender, high and low (slant-flat, hitch-seam, double slants) or inside and outside (stick), or both (snag’s triangle), and the quarterback throws to the one that defender doesn’t take. Spacing stretches the whole underneath coverage; the speed out beats one cornerback’s alignment.
  • Most of the decision is made before the snap: read the defender’s leverage, cushion and help, and throw away from leverage.
  • Press is the defense’s answer to the quick game’s timing; the receiver’s answer is the release, stem, stack and break, and the double move punishes a defender who jumps the short route.
  • Now and bubble screens punish soft and outnumbered alignments (numbers, leverage, grass). Against the blitz, the free rusher is the quarterback’s, and the ball that comes out fast, into the space the blitzer left, is worth the most.

Connections

Builds on: Passing Fundamentals (the routes, drops and stretches every concept here is made of) and Pass Protection (hot routes, sight adjustments and the five-, six- and seven-man protections the quick game leans on), with the defensive basics of A First Look at Defense.

Sets up: Dropback Concepts takes the same stretches deeper, with more time and longer progressions; Run-Pass Options attaches the quick throws and screens to runs; Quarterback Play covers anticipation, footwork and the numbers for judging accuracy. On the defensive side, Coverage Fundamentals teaches press and off coverage from the cornerback’s side, and Blitz and Man Pressure does the blitz math from the defense’s. Concepts Versus Coverages finally pairs each concept with the coverages it beats, and Third Down shows where the quick game earns its keep.

Case studies: Walsh’s 49ers and the West Coast offense and Andy Reid’s Chiefs.

NoteGo deeper
  • Bill Walsh, with Brian Billick and James A. Peterson, Finding the Winning Edge (1997): the coach’s own account of how he built a passing game around timing and short throws.
  • Chris B. Brown, The Art of Smart Football (2015) and The Essential Smart Football (2012), and his Smart Football site: clear essays on stick, snag, spacing and why they work.
  • Tim Layden, Blood, Sweat and Chalk (2010): the chapter on Walsh traces the West Coast offense back to the Cincinnati years.
  • 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) records every player ten times a second, and its play and player files include each play’s time to throw and each receiver’s charted route, so you can find real quick throws 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")
# completed passes out in under 2.5 seconds on which some receiver ran a slant
quick = plays[(plays["timeToThrow"] < 2.5) & (plays["passResult"] == "C")]
slants = player_play.loc[player_play["routeRan"] == "SLANT", ["gameId", "playId"]].drop_duplicates()
cand = quick.merge(slants, on=["gameId", "playId"])
g, pl = cand.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, 0.5, 1.0, 1.6])   # snap, release, break, catch

  1. Course calculation from nflverse participation data (time_to_throw, FTN-sourced), 2025 regular season: 17,394 pass attempts with a recorded time (sacks, spikes and scrambles excluded). Median 2.5 seconds; 48% of attempts left the quarterback’s hand in under 2.5 seconds and 52% in 2.5 seconds or less. The 2.5-second line is an analysts’ convention, not a rule; PFF, for one, splits time to throw there (PFF, “Signature Stat Snapshot: Time To Throw”: https://www.pff.com/news/signature-stat-snapshot-time-to-throw).↩︎

  2. Course calculation, same data: attempts out in under 2.5 seconds were completed 72.2% of the time (success rate 51.5%), slower attempts 57.1% (success rate 44.9%). Success means positive EPA, the definition in Expected Points, EPA and Success Rate.↩︎

  3. Walsh to Tom FitzGerald of the San Francisco Chronicle: “We couldn’t control the football with the run; [opposing] teams were just too strong. So it had to be the forward pass, and obviously it had to be a high-percentage, short, controlled passing game.” The same profile describes his offense as “passes so short they were effectively extended handoffs.” Both in “Walsh, Bill,” Encyclopedia.com: https://www.encyclopedia.com/people/sports-and-games/sports-biographies/bill-walsh↩︎

  4. Course calculation, same data: on 2025 completions thrown in under 2.5 seconds, 58.7% of receiving yards came after the catch; on slower completions, 38.5%. Screens are included, which pushes the quick figure up: about 23% of throws out in under 2 seconds were screens (FTN is_screen_pass).↩︎

  5. Bill Walsh with Brian Billick and James A. Peterson, Finding the Winning Edge (Sports Publishing, 1997), on building the Bengals’ short passing game around Virgil Carter. “West Coast offense,” Wikipedia: Walsh “was tasked with devising an offensive plan suited to Bengal quarterback Virgil Carter, who had an accurate but relatively weak arm,” and Carter “led the league in passing percentage in 1971”: https://en.wikipedia.org/wiki/West_Coast_offense . Carter completed 138 of 222 (62.2%), the NFL’s best that season: Pro Football Reference, 1971 NFL leaders, https://www.pro-football-reference.com/years/1971/leaders.htm . Greg Cook tore the rotator cuff in his throwing shoulder in the third game of 1969, played through the season, then needed surgeries that kept him out of the next three seasons: “Greg Cook,” Wikipedia, https://en.wikipedia.org/wiki/Greg_Cook . The Bengals began play in 1968.↩︎

  6. Virgil Carter and Robert E. Machol, “Operations Research on Football,” Operations Research 19, no. 2 (1971): 541–544, which estimated the expected-point value of first-and-10 at each part of the field from 8,373 plays in the first 56 games of the 1969 NFL season: https://ideas.repec.org/a/inm/oropre/v19y1971i2p541-544.html . On Carter’s statistics major at BYU and his MBA at Northwestern, taken while he played for the Bears: ESPN, “Ex-Bengals QB Virgil Carter’s legacy of EPA, other analytics” (2024): https://www.espn.com.au/nfl/story/_/id/40276150/nfl-epa-analytics-virgil-carter-cincinnati-bengals↩︎

  7. Pro Football Hall of Fame, “Bill Walsh” (Class of 1993): head coach of the 49ers 1979–1988, winners of Super Bowls XVI, XIX and XXIII: https://www.profootballhof.com/players/bill-walsh/↩︎

  8. Pro Football Hall of Fame, “Jerry Rice” (Class of 2010), including his 22 touchdown catches in the strike-shortened 12-game 1987 season, then an NFL record (Randy Moss caught 23 in 2007): https://www.profootballhof.com/players/jerry-rice/↩︎

  9. Pro Football Hall of Fame, NFL history, 1978: “Rules changes permitted a defender to maintain contact with a receiver within five yards of the line of scrimmage, but restricted contact beyond that point”: https://www.profootballhof.com/football-history/football-history/1960-1979/1978 ; and 1977: “Defenders were permitted to make contact with eligible receivers only once”: https://www.profootballhof.com/football-history/football-history/1960-1979/1977 . The 1978 change is often called the “Mel Blount rule” (Steelers Depot, “Mel Blount explains play that created Mel Blount rule,” 2024: https://steelersdepot.com/2024/02/mel-blount-explains-play-that-created-mel-blount-rule). The era is the subject of The 1978 Liberation.↩︎

  10. Ralph D. Russo, “Air Raid evolution: Pioneering offense passes test of time,” Associated Press, Aug. 24, 2022, on the offense “birthed from the mind of Hal Mumme” and “inspired by LaVell Edwards’ BYU passing game and Bill Walsh’s West Coast offense,” whose “foundational concepts and plays” it lists as “mesh, Y cross, four verts, the quick game”: https://kvia.com/ap-texas/2022/08/24/air-raid-evolution-pioneering-offense-passes-test-of-time/↩︎

  11. NFL Video Rulebook, “Offensive Pass Interference”: it is offensive pass interference for an offensive player to block more than one yard beyond the line of scrimmage before the pass is thrown, or to initiate contact with a defender in a way that restricts him while the pass is in the air: https://operations.nfl.com/the-rules/nfl-video-rulebook/offensive-pass-interference . The rule is NFL Rule 8, Section 5 (Pass Interference); see https://operations.nfl.com/the-rules/nfl-rulebook/ .↩︎

  12. NFL Rulebook, Rule 8, Section 4 (“Legal and Illegal Contact With Eligible Receivers”), Articles 1–3: within the area five yards beyond the line of scrimmage, a defender may chuck an eligible receiver in front of him; beyond five yards, while the passer is still in the pocket with the ball, he may not initiate contact with a receiver who is trying to evade him (illegal contact: five yards and an automatic first down). Grabbing or restricting a receiver is defensive holding (Rule 12, Section 1): https://operations.nfl.com/the-rules/nfl-rulebook/ ; NFL Video Rulebook, “Illegal Contact”: https://operations.nfl.com/the-rules/nfl-video-rulebook/illegal-contact . After the Patriots’ physical coverage in the 2003 season’s AFC Championship Game, the Competition Committee made illegal contact and defensive holding a point of emphasis for 2004: NBC Sports Boston, “They called it the ‘Ty Law Rule’”: https://www.nbcsportsboston.com/nfl/new-england-patriots/they-called-it-the-ty-law-rule-and-that-was-ok-with-him/393549/↩︎

  13. Course calculation from FTN charting (is_contested_ball) joined to nflverse play-by-play, 2025 regular season: 16,733 pass attempts excluding throwaways and spikes; 13.9% charted as contested. Completion rate 41.9% on contested throws, 71.1% on the rest. Contested throws went farther (12.7 yards in the air on average, against 7.1), which accounts for part of the gap.↩︎

  14. NFL Rulebook, Rule 8, Section 7 (“Backward Pass and Fumble”): players of either team may catch a backward pass, or recover one after it touches the ground, and advance it; the special fourth-down and final-two-minutes limits on advancing apply to fumbles, not backward passes: https://operations.nfl.com/the-rules/nfl-rulebook/ ; NFL Video Rulebook, “Backward Pass”: https://operations.nfl.com/the-rules/nfl-video-rulebook/backward-pass↩︎

  15. Course calculation from nflverse play-by-play and participation data (number_of_pass_rushers, time_to_throw), 2025 regular season dropbacks with a charted rush count. “Quick” = a pass attempt out in under 2.5 seconds; “held” = an attempt out in 2.5 seconds or more, a sack, or a scramble. EPA per dropback: four or fewer rushers, quick +0.118 (n = 5,767) and held −0.014 (n = 8,708); five or more rushers, quick +0.226 (n = 2,532) and held −0.098 (n = 2,763). Five or more rushed on 27% of these dropbacks. Pooled over all outcomes, EPA per dropback was +0.054 against five or more rushers (n = 5,306; sack rate 7.8%) and +0.038 against four or fewer (n = 14,524; sack rate 6.0%); success rates 45.9% and 45.5%. Five or more rushed on 25% of first-down, 25% of second-down, 30% of third-down and 34% of fourth-down dropbacks, so the pooled comparison is not controlled for down and distance.↩︎

  16. Pro Football Reference, Drew Brees career passing (2017: 386 of 536, 72.0%; 2018: 364 of 489, 74.4%), and the NFL record book (single-season completion percentage): https://www.pro-football-reference.com/players/B/BreeDr00.htm . Guinness World Records lists 74.4% as the record (and 72.0% in 2017 as the mark it broke): https://www.guinnessworldrecords.com/world-records/93395-highest-completion-percentage-nfl-season . In nflverse play-by-play no qualified passer from 2019 through 2025 topped 74.4% (the closest was Brees himself, about 74% in 2019). Payton was the Saints’ head coach from 2006 to 2021 but was suspended for the 2012 season.↩︎

  17. NFL.com, “Saints’ Michael Thomas sets NFL reception record” (Dec. 22, 2019), his 144th catch passing Marvin Harrison’s 143 (2002): https://www.nfl.com/news/saints-michael-thomas-sets-nfl-reception-record-0ap3000001090697 ; he finished with 149 (NFL.com, “Saints WR Michael Thomas named 2019 NFL OPOY”: https://www.nfl.com/news/saints-wr-michael-thomas-named-2019-nfl-opoy-0ap3000001100165). Course calculations from nflverse play-by-play and participation (NGS-sourced) data: 104 of Thomas’s 149 catches (70%) came on throws of less than 10 air yards, and half within 5; Brees’s 2019 time to throw averaged 2.57 s, second-fastest of 26 passers with 300+ attempts, with an average depth of target of about 6.5 yards.↩︎

  18. Pro Football Reference, Wes Welker career receiving: 112 receptions (2007, tied for the NFL lead), 123 (2009, NFL lead), 122 (2011, NFL lead): https://www.pro-football-reference.com/players/W/WelkWe00.htm . The 2007 lead was shared with Cincinnati’s T. J. Houshmandzadeh (also 112). In nflverse play-by-play, his targets in 2009–2012 averaged about 5.5 to 7.7 yards in the air, against a league average of about 8.2 to 8.7, and in 2007, 2009 and 2011 between 51% and 65% of his catches came on throws of 5 air yards or less.↩︎

  19. Course calculation from nflverse participation data (time_to_throw, NGS-sourced), 2022 regular season, passers with 300 or more attempts: Brady averaged 2.45 seconds, fastest of 29. He was second-fastest in 2021.↩︎

  20. Course calculation from nflverse participation data (defense_coverage_type, NGS-charted), regular-season dropbacks with a coverage label: Kansas City faced a two-high coverage (Cover 2, Cover 4, Cover 6 or 2-Man) on 45.8% of dropbacks in 2021 (3rd of 32), against a league average of 38.2%; in 2018 the figures were 35.6% and 32.9%. Kansas City’s average depth of target fell from 9.06 yards in 2018 (5th deepest) to 8.55 in 2019 (13th), 8.11 in 2020 (15th) and 7.36 in 2021 (27th).↩︎

  21. NFL.com, “Chiefs trading WR Tyreek Hill to Dolphins for multiple draft picks, including 2022 first-rounder” (March 23, 2022): five picks, the 2022 first-, second- and fourth-rounders and 2023 fourth- and sixth-rounders: https://www.nfl.com/news/chiefs-trading-wr-tyreek-hill-to-dolphins-for-multiple-draft-picks↩︎

  22. Course calculation from nflverse play-by-play, regular season: Kansas City’s average depth of target was 9.06 yards in 2018 (5th deepest of 32), 7.24 in 2022 (23rd), 6.47 in 2023 and 6.37 in 2024 (31st both years), and 7.80 in 2025 (17th). Travis Kelce drew 153 targets in 2022, 72.5% of them under 10 air yards.↩︎

  23. nflverse play-by-play and participation (NGS charting), 2022 postseason, game 2022_22_KC_PHI: 4th quarter, 9:26, third-and-goal at the Philadelphia 4, “P.Mahomes pass short left to S.Moore for 4 yards, TOUCHDOWN,” time to throw 1.735 s, coverage charted COVER_0 (man), seven pass rushers; the Toney touchdown came at 12:08 (5 yards, time to throw 1.267 s). On the two plays as the same motion concept (“Corn Dog”) run to opposite sides: CBS Sports, “‘Corn dog’ joins Chiefs Super Bowl lore: Andy Reid says Kadarius Toney TD came on a play with an unusual name”: https://www.cbssports.com/nfl/news/corn-dog-joins-chiefs-super-bowl-lore-andy-reid-says-kadarius-toney-td-came-on-a-play-with-an-unusual-name ; Yahoo Sports, “The Xs and Os: How the Chiefs won the Super Bowl with a couple of ‘Corn Dogs’”: https://sports.yahoo.com/xs-os-chiefs-won-super-110132971.html↩︎