Personnel: Who’s on the Field Before Anything Else

Two digits, said before the huddle breaks, that tell you who the offense brought, what the defense will send back, and how the odds of a run or a pass have already moved.

Show the Python (shared helpers for this chapter’s diagrams)
import os
import re

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import animation
from matplotlib.lines import Line2D
from matplotlib.patches import Circle, FancyBboxPatch, Rectangle
from IPython.display import HTML, display

from gridiron import Play, Player, Field, defense, routes as r, style, draw_marker
from gridiron.field import FIELD_WIDTH
from gridiron.formations import LINE_D, OFF_BALL, TE_W, WING_W, OL_SPLIT

style.apply()
PDF = os.environ.get("RTG_FORMAT", "").lower() in ("pdf", "typst", "print")

RB_RING = style.THIRD          # aqua ring: a running back (tailback or fullback)
TE_RING = style.LINE_TO_GAIN   # gold ring: a tight end
OL_RING = "#4a3aa7"            # purple ring: a sixth offensive lineman
UC, GUN, PISTOL, FB_D, TB_D = -1.9, -5.0, -3.5, -4.5, -7.0   # QB and back depths (yards)


def line5():
    """The five offensive linemen; only the center is lettered."""
    return [Player(k, k, "off", LINE_D, w, label=("C" if k == "C" else ""))
            for k, w in [("LT", -2 * OL_SPLIT), ("LG", -OL_SPLIT), ("C", 0.0),
                         ("RG", OL_SPLIT), ("RT", 2 * OL_SPLIT)]]


def P(key, pos, d, w, label=None):
    """One offensive skill player at (d, w): d = yards from the ball (negative = backfield),
    w = yards to the offense's right."""
    return Player(key, pos, "off", d, w, label=label or key)


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


def ring_by_body(p, skip=()):
    """Ring every back (aqua) and tight end (gold): count the rings and you have the personnel."""
    for k, pl in p.players.items():
        if k in skip or pl.side != "off":
            continue
        if pl.pos in ("RB", "FB"):
            p.highlight(k, color=RB_RING)
        elif pl.pos == "TE":
            p.highlight(k, color=TE_RING)
    return p


def body_count(players):
    off = [q for q in players if q.side == "off"]
    rb = sum(q.pos in ("RB", "FB") for q in off)
    te = sum(q.pos == "TE" for q in off)
    wr = sum(q.pos == "WR" for q in off)
    return rb, te, wr


def card_title(ax, code, players):
    rb, te, wr = body_count(players)
    ax.text(0.0, 1.03, f"{code} personnel", transform=ax.transAxes, ha="left", va="bottom",
            fontsize=10, fontweight="bold", color=style.INK)
    ax.text(1.0, 1.03, f"{rb} RB · {te} TE · {wr} WR", transform=ax.transAxes, ha="right",
            va="bottom", fontsize=8, color=style.INK_2)


def ring_legend(fig, extra=(), y=0.0, ncol=4):
    h = [Line2D([], [], marker="o", ls="", ms=9, markerfacecolor=style.OFFENSE, markeredgecolor=RB_RING,
                markeredgewidth=2.2, label="running back (R, F)"),
         Line2D([], [], marker="o", ls="", ms=9, markerfacecolor=style.OFFENSE, markeredgecolor=TE_RING,
                markeredgewidth=2.2, label="tight end"),
         Line2D([], [], marker="o", ls="", ms=9, markerfacecolor=style.OFFENSE, markeredgecolor="white",
                label="no ring: wide receiver, QB (Q), line")]
    h += list(extra)
    fig.legend(handles=h, loc="lower center", bbox_to_anchor=(0.5, y), ncol=ncol, fontsize=7.5,
               frameon=False, handletextpad=0.3, columnspacing=1.2)


def html_video(anim):
    """Embed a matplotlib animation in the HTML build (mp4 if ffmpeg exists, else a JS player)."""
    import shutil
    if shutil.which("ffmpeg"):
        html = anim.to_html5_video(embed_limit=40).replace("<video ", '<video style="max-width:100%;height:auto" ')
    else:
        html = anim.to_jshtml(default_mode="once")
    plt.close(anim._fig)
    display(HTML(html))


# --- The personnel "card deck": one neutral, legal alignment per grouping (strength to the right).
# Every card has seven men on the line: the five linemen plus the two players at the ends.
CARDS = {
    "11": [P("QB", "QB", UC, 0, "Q"), P("RB", "RB", TB_D, 0, "R"), P("Y", "TE", LINE_D, TE_W),
           P("X", "WR", LINE_D, -12.5), P("H", "WR", OFF_BALL, -7.5), P("Z", "WR", OFF_BALL, 12.5)],
    "12": [P("QB", "QB", UC, 0, "Q"), P("RB", "RB", TB_D, 0, "R"), P("Y", "TE", LINE_D, TE_W),
           P("U", "TE", LINE_D, -TE_W), P("X", "WR", OFF_BALL, -12.5), P("Z", "WR", OFF_BALL, 12.5)],
    "21": [P("QB", "QB", UC, 0, "Q"), P("FB", "FB", FB_D, 0, "F"), P("RB", "RB", TB_D, 0, "R"),
           P("Y", "TE", LINE_D, TE_W), P("X", "WR", LINE_D, -12.5), P("Z", "WR", OFF_BALL, 12.5)],
    "13": [P("QB", "QB", UC, 0, "Q"), P("RB", "RB", TB_D, 0, "R"), P("Y", "TE", LINE_D, TE_W),
           P("U", "TE", LINE_D, -TE_W), P("H", "TE", -1.7, 7.3), P("X", "WR", OFF_BALL, -12.5)],
    "10": [P("QB", "QB", GUN, 0, "Q"), P("RB", "RB", GUN, -2.2, "R"), P("X", "WR", LINE_D, -12.5),
           P("H", "WR", OFF_BALL, -7.5), P("Y", "WR", OFF_BALL, 7.5), P("Z", "WR", LINE_D, 12.5)],
    "20": [P("QB", "QB", GUN, 0, "Q"), P("RB", "RB", GUN, -2.2, "R"), P("FB", "RB", GUN, 2.2, "F"),
           P("X", "WR", LINE_D, -12.5), P("H", "WR", OFF_BALL, 7.5), P("Z", "WR", LINE_D, 12.5)],
    "22": [P("QB", "QB", UC, 0, "Q"), P("FB", "FB", FB_D, 0, "F"), P("RB", "RB", TB_D, 0, "R"),
           P("Y", "TE", LINE_D, TE_W), P("U", "TE", LINE_D, -TE_W), P("Z", "WR", OFF_BALL, 12.5)],
    "00": [P("QB", "QB", GUN, 0, "Q"), P("X", "WR", LINE_D, -13.0), P("H", "WR", OFF_BALL, -8.0),
           P("Y", "WR", OFF_BALL, 5.8), P("F", "WR", OFF_BALL, 9.5), P("Z", "WR", LINE_D, 13.2)],
}


def hide_yard_numbers(ax):
    """Drop the faint painted field numbers (they sit where the receivers stand in these zoomed cards)."""
    for t in list(ax.texts):
        if t.get_text().isdigit() and t.get_alpha() is not None and abs(t.get_alpha() - 0.55) < 1e-6:
            t.remove()


def card_deck(codes):
    fig, axes = plt.subplots(2, 2, figsize=(6.6, 4.0))
    for ax, code in zip(axes.flat, codes):
        players = line5() + CARDS[code]
        p = ring_by_body(Play(players, None, los=35, to_go=None))
        p.draw(ax=ax, lateral=(-15.5, 15.5), window=(-9.5, 2.6), title=False)
        hide_yard_numbers(ax)
        card_title(ax, code, players)
    fig.subplots_adjust(left=0.01, right=0.99, top=0.93, bottom=0.11, hspace=0.42, wspace=0.05)
    ring_legend(fig, ncol=3)
    return fig

Third-and-1 at midfield, early in the fourth quarter. The previous play has barely been whistled dead when the broadcast cuts wide: two wide receivers jog toward their own sideline, and two bigger men, a tight end and a fullback, run the other way to join the huddle. Across the field, the defense reacts within a couple of seconds. A cornerback sprints off, a 245-pound linebacker sprints on, and someone near the defensive bench is shouting a single word. The ball hasn’t been snapped, the offense hasn’t even lined up, and the analyst says “they’ve gone to 22 personnel.” He’s right, and both teams have already made a move. This chapter is about that move: who the offense chooses to put on the field, how the defense answers (and why it swapped only one player when the offense swapped two), who that shouting man is, and how much those two digits tell you about what’s coming.

You’ll be able to…

Eleven players, but only five that change

Start with a puzzle. An offense has eleven players on the field, and the label for who they are has only two digits. How can two digits describe eleven people?

Because six of the eleven almost never change. Every normal offensive snap has a quarterback and five offensive linemen (the center, two guards and two tackles, the big men who block and, by rule, can’t catch a forward pass). Those six are the fixed core. The other five spots are the variables, and they can be filled by three kinds of player that The Twenty-Two introduced as the skill positions:

  • Running backs. Players who line up behind the line, mostly to carry the ball or block. The tailback (or halfback) is the runner; the fullback is a bigger back who mostly blocks for him. Both count as running backs here.
  • Tight ends. Big, versatile players, usually 245 to 265 pounds, who often line up right next to a tackle. They block like small linemen and catch passes.
  • Wide receivers. Usually the lightest and fastest on the offense, lined up wide, mainly to catch passes.

Every way of filling those five spots is a personnel grouping: the mix of running backs, tight ends and wide receivers on the field for a play. Coaches, scouts, broadcasters and the data all write it the same way, as two digits:

First digit = running backs. Second digit = tight ends. Wide receivers = 5 − backs − tight ends.

So “one-one,” usually just said “eleven,” means one back and one tight end, which leaves three wide receivers. “Twelve” (one-two) is one back and two tight ends, so two receivers. “Twenty-one” is two backs, one tight end and two receivers. You never need a third digit, because the receivers are whatever is left over. (One grouping, jumbo, bends the arithmetic; it gets its own section below.)

The digits are the shared language of scouting reports, broadcasts and data. Inside a team’s own playbook, groupings often carry word names instead, and the words differ from team to team (one Houston Texans explainer mentions “Pony” and “Rabbit” for kinds of two-back grouping).1 When you hear an unfamiliar word on a sideline microphone, it is probably a team’s private name for one of the groupings below.

CautionCommon misconception

The digits are not jersey numbers, and “11 personnel” does not mean “the eleven players.” It is a count of body types: one running back, one tight end. Say it “eleven” or “one-one,” but read it as two separate digits. Defensive labels count differently: a “4-2-5” means four linemen, two linebackers and five defensive backs. Older names such as “4-3” leave off the defensive backs because four was taken for granted. A First Look at Defense owns those names.

Why count only backs and tight ends? Because those are the two body types that change what the offense can do and what the defense must fear. A tight end is a blocker who can run routes; a fullback is a blocker who can carry the ball or catch it out of the backfield. Each one you add makes the offense bigger and better at running the ball, and takes away a wide receiver, the player most dangerous in the open field. The two digits are, in effect, a dial between “big” and “fast.”

NoteMadden vs. real life

In Madden you pick a formation and the right players appear in it, and the computer’s defense swaps its own players instantly. In the NFL, personnel is a separate decision made before the formation and the play. A coach shouts or signals the grouping (“Twelve! Twelve!”), the players in that package run on, and the defense has to see them, decide, and send its own substitutes. It takes several seconds of the forty-second play clock, in plain view: the first thing you can read.

Reading the deck: the eight groupings you’ll meet

Here are the eight groupings the chapter asks you to know, each drawn in an ordinary, legal alignment. Don’t worry yet about the names of the alignments. Those are formations, the next chapter’s job. For now, count the rings: an aqua ring is a running back, a gold ring is a tight end, and anyone with no ring (apart from the quarterback and the linemen) is a wide receiver.

Show the Python
card_deck(["11", "12", "21", "13"])
plt.show()
Figure 9.1: The four everyday groupings. Count the rings: aqua = running backs (R is the tailback, F the fullback), gold = tight ends; unringed players outside the line are wide receivers (X, Z, H). In 13 personnel the third tight end stands just outside and behind the right tight end (a ‘wing’) and wears the letter H: the letters name spots in the formation, not body types, which is why the ring, not the letter, tells you what he is. Seven men are on the line in every card: the five linemen (only the center, C, is lettered) plus the two players at the ends.

The letters on the receivers are the names most playbooks use for their spots: X is the receiver usually on the line on one side (the “split end”), Z the receiver off the line on the other side (the “flanker”), Y usually the tight end, and H or F a player the offense moves around, who can be a slot receiver, a second tight end or a fullback depending on the grouping. Look at the 12 card and you’ll see X standing off the line: with a tight end on the line at each end, both receivers have to step back so the tight ends stay the end men, and eligible. Formation Rules and the Language of Alignment explains the letters and that rule properly. In this chapter they’re just name tags; the rings do the counting.

11 personnel (1 RB, 1 TE, 3 WR) is the NFL’s default: it was on the field for about six plays in ten every season from 2016 to 2025. Its appeal is that it can do almost anything. Three wide receivers stretch the defense across the field; the one tight end can stay in to block or run a route; the one back can run, block a blitzer or catch. Because the offense can run or pass from it with almost equal ease, it gives the defense very little to go on. It also forces the defense to answer with a fifth defensive back, which we’ll see is the whole substitution game in miniature.

12 personnel (1 RB, 2 TE, 2 WR) swaps the third receiver for a second tight end. Now the offense can line up with a blocker next to both tackles and run either way, or split one of the tight ends out like a receiver and throw. It is the league’s second most common grouping and, as the data below shows, the closest thing football has to a coin flip.

21 personnel (2 RB, 1 TE, 2 WR) adds a fullback, the old-fashioned lead blocker. This is the grouping of the classic I-formation (fullback directly in front of the tailback) and of the old pro set (two backs side by side behind the quarterback), the standard NFL look of an earlier era. A good fullback gives the offense an extra blocker who starts moving before the defense knows where the play is going.

13 personnel (1 RB, 3 TE, 1 WR) is three tight ends and just one receiver. It is mostly a short-yardage and running grouping, though as you’ll see in the film room, one team in 2025 turned it into a weapon for both.

Show the Python
card_deck(["10", "20", "22", "00"])
plt.show()
Figure 9.2: Four groupings at the edges. 10 and 00 personnel are spread groupings, so the quarterback is in the shotgun (about five yards behind the center) and wide receivers fill the Y and F spots that a tight end or back would fill in other groupings. 20 personnel puts two backs beside the quarterback. 22 personnel is the old power look: fullback, tailback and a tight end on each side, with one receiver.

22 personnel (2 RB, 2 TE, 1 WR) is the heavy end of the everyday menu: a fullback, a tailback and two tight ends, leaving one receiver. The offense is telling the defense, more or less, “we’re going to run, and we’re bringing extra people to block for it.” That’s useful near the goal line, on third-and-1, and late in a game when the offense wants to keep the clock running. It is also, for exactly that reason, a great place to fake a run and throw (more on that trade below).

10 personnel (1 RB, 0 TE, 4 WR) drops the tight end for a fourth receiver. It’s everywhere in college football, where spread offenses grew up, but rare in the NFL (around 1% of plays in recent seasons). NFL offenses want a tight end’s blocking against much bigger pass rushers, and four receivers are easy for a defense to answer: it just sends in a sixth defensive back. A good tight end is harder to match, because no substitute is both big enough to take on his block and fast enough to cover him.

20 personnel (2 RB, 0 TE, 3 WR) puts two backs next to the quarterback in the shotgun with three receivers. It’s a niche look (about 1% of NFL plays or less), but a handy one: either back can take a handoff, block, or slip out for a pass. 00 personnel is five wide receivers and no back at all, an “empty” backfield with nobody beside the quarterback. In the NFL it’s so rare that you may not see it all season.2 (Empty formations, in which the back or tight end lines up wide, are common. That’s a formation, not a personnel grouping, and it’s the subject of Spread Formations.)

There are a handful of other combinations, such as 01, 02, 23 and the occasional 30, but together they make up only a percent or two of plays. If you can read the eight above, you can read practically every NFL snap.

Personnel is who, not where

One more rule before we move on, and it’s the one beginners trip over. Personnel counts who the players are, not where they stand. A tight end who lines up out wide like a receiver is still a tight end, so the grouping is still 12 if there are two of them. A running back who lines up in the slot is still a running back. A wide receiver standing in the backfield is still a receiver.

That’s not pedantry; it’s why personnel matters. The defense chooses its players by who comes on the field, and only then does the offense line up. Send in two tight ends, get bigger defenders; then spread those tight ends out like receivers, and bigger, slower players have to cover them. That gap between personnel and formation is one of the oldest tricks in football, and you can’t see it unless you count first.

The hard case is a player whose body doesn’t match his label. Deebo Samuel was a 49ers wide receiver who, in 2021, also carried the ball 59 times for 365 yards and eight touchdowns.3 With him in the backfield, a grouping that reads “11” on paper played like two backs. The Saints have used Taysom Hill, a quarterback, as a runner, a receiver and a tight end; the data lists him as a quarterback in 2023 and 2025 and as a tight end in 2024, so the same kind of snap gets a different label in different seasons. The Ravens’ Patrick Ricard has played both defensive line and fullback. Offenses use players like these on purpose: the defense picks its substitutes by label, and a player who is two things at once makes every answer partly wrong. Note it in your log whenever you see one.

CautionCommon misconception

“You can tell the position by the jersey number.” Not reliably, not any more. Before 2021, NFL tight ends wore numbers in the 80s (or 40s), and receivers wore 80s or 10–19. Since the league loosened its numbering rules in 2021 and again in 2023, tight ends, receivers and backs can all wear single digits and most of the same ranges4 (The Twenty-Two has the details). On TV, use three other clues. Size: tight ends are usually 245 to 265 pounds, receivers well under that. Stance: a tight end next to the tackle usually sets in a three-point stance, one hand down like a lineman, while receivers stand up. Homework: a glance at the team’s roster before kickoff, plus the analyst, who will often name the grouping for you.

Heavy personnel

You’ll hear broadcasters say an offense “went heavy.” Heavy personnel means any grouping with fewer than three wide receivers: the offense has taken receivers off and put tight ends, backs or linemen on. So 12, 21, 13 and 22 are all heavy (some people keep the word for 13, 22 and jumbo; this course uses the wider meaning, as NFL Next Gen Stats does). Why take a fast player off the field? To block more people, since each tight end or fullback is an extra blocker who can also be a threat; to make the defense get bigger, and so slower in coverage; and to hide the play, because a defense facing players who can run and catch can’t relax against either.

Jumbo: when a sixth lineman comes in

Sometimes even three tight ends isn’t big enough. In a jumbo grouping, the offense puts a sixth offensive lineman on the field, usually lined up exactly where a tight end would be, at the end of the line next to a tackle. (Some teams put him inside instead, with two tackles side by side on one end of the line; that unbalanced line is a formation trick for a later chapter.) He’s a lineman, often 300 pounds or more, so he blocks much better than any tight end. That’s the point: on the goal line, on third-and-1, or when killing the clock with a lead, the offense wants raw size.

Jumbo is where the two-digit arithmetic needs one adjustment. The sixth lineman takes one of the five changeable spots, so only four are left for backs, tight ends and receivers: receivers = 4 − backs − tight ends. The jumbo below has one back and two tight ends, so one receiver, not two.

Show the Python
off = line5() + [
    Player("E6", "OL", "off", LINE_D, -TE_W, label="79"),
    P("QB", "QB", UC, 0, "Q"), P("RB", "RB", -6.5, 0, "R"),
    P("Y", "TE", LINE_D, TE_W), P("U", "TE", -1.6, 7.2),      # a legal wing, clear of Y's ring
    P("X", "WR", OFF_BALL, -11.0),
]
gl = [D("LE", "DE", 1.0, -5.9), D("DLT", "DT", 1.0, -2.4), D("NT", "NT", 1.0, 0.0),
      D("DRT", "DT", 1.0, 2.4), D("RE", "DE", 1.0, 5.9),
      D("WILL", "WILL", 3.4, -3.4), D("MIKE", "MIKE", 3.4, 0.6), D("SAM", "SAM", 3.0, 7.4),
      D("LCB", "CB", 1.5, -11.0), D("SS", "SS", 4.6, 10.5), D("FS", "FS", 6.5, -2.0)]
p = Play(off, gl, los=108, to_go=2)
ring_by_body(p)
p.highlight("E6", color=OL_RING)
p.note("6th lineman", at=(-2.7, -5.2))          # directly behind #79
p.draw(lateral=(-15.5, 15.5), window=(-9.5, 9.5), title=False)
hide_yard_numbers(plt.gca())
plt.show()
Figure 9.3: Jumbo on the 2-yard line: six offensive linemen (the sixth, #79, ringed in purple, stands at the left end where a tight end would), two tight ends on the right (gold), one back (aqua) and one receiver (X). Six linemen + 1 back + 2 tight ends + 1 receiver + the quarterback = 11. The defense answers with its own goal-line package: five linemen (E = end, T = tackle, N = nose tackle), three linebackers (S, M, W) and three defensive backs (C = cornerback, SS and FS = safeties). To be allowed to catch a pass, #79 must first report to the referee as eligible.

There is a catch, and it is a rule, not a strategy. Linemen wear numbers in the 50–79 range, and a player wearing one of those numbers is ineligible to catch a forward pass (he can block, but not go out for a ball thrown past the line) unless he reports to the referee before the snap. The referee then announces it, often over the stadium microphone (“Number 79 is reporting as eligible”), so the defense knows. The full rules for eligibility and reporting are in Formation Rules, which owns them; for now it’s enough to know that a jumbo lineman is a blocker by default and a receiver only by announcement.

So is the picture above “12” or “13”? It depends who’s counting. The data used in this chapter labels players by roster position, so it calls this snap “12” (one back, two tight ends) plus an extra lineman, and by the jumbo rule that leaves one receiver. Some charters and staffs count a lineman standing at tight end as a tight end and call it “13”; others write “12 jumbo” or “6 OL.” In the charts below, jumbo plays keep the code their backs and tight ends give them, and are also counted separately as jumbo.

Jumbo is the most predictable grouping in football. In neutral situations from 2023 to 2025, offenses in jumbo threw on fewer than one play in three. That predictability is the cost: when the defense knows a run is coming, it can load up for it. It is also the opportunity, as the Detroit Lions showed, because a defense that is sure of the run is the easiest one to fool with a pass.

NoteFilm room: Detroit Lions 2022–2024

Under offensive coordinator Ben Johnson (2022–2024), the Lions made the eligible lineman a regular part of their offense. Backup tackle Dan Skipper was the usual extra lineman, and he or, on the odd trick play, starting left tackle Taylor Decker could report eligible. In 2022 Detroit used a sixth lineman on about 10% of its snaps, second-most in the league.

The most famous of those snaps didn’t count. On December 30, 2023, at Dallas, Detroit scored a touchdown with 23 seconds left to pull within a point at 20–19 and went for two. Jared Goff threw to Decker, who caught it in the end zone. The officials ruled that it was Skipper, not Decker, who had reported eligible, so Decker was an ineligible receiver. The conversion was wiped out and Detroit had to try again from farther back: the next attempt was intercepted but erased by a Dallas offside penalty, the one after that fell incomplete, and the Cowboys won 20–19.5 The play became a national argument about reporting, and it shows the logic of jumbo: the defense saw six linemen and expected a run; the Lions’ answer was a pass to a lineman.

What to notice: when an offense goes jumbo, watch the referee. If he turns to the press box and announces a number, a lineman has reported eligible, and the defense now has to account for him too.

The defense answers: substitution matching

Personnel is a two-sided game. The offense picks its eleven first, and the defense answers with its own. Defensive groupings are named differently, usually by how many defensive backs (cornerbacks and safeties, the players whose main job is pass coverage) are on the field, because that number is what the defense changes most:

  • Base: four defensive backs, with seven players up front (four linemen and three linebackers in a “4-3,” or three and four in a “3-4”). Built to stop the run.
  • Nickel: five defensive backs. The fifth, called the nickel back or slot cornerback, usually replaces a linebacker and covers the receiver lined up in the slot (between the outside receiver and the line).
  • Dime: six defensive backs, for obvious passing downs.

A First Look at Defense builds these packages properly.

The defense’s choice follows a simple logic: match bodies to bodies. A wide receiver is fast, so he should be covered by a defensive back, who is also fast. A tight end or fullback is big, so a linebacker, who is bigger and better at taking on blocks, is a reasonable match. More receivers, more defensive backs; more tight ends and backs, more linebackers. That reflex is called substitution matching.

It is a direction, not a one-for-one swap, because the defense has a floor of four defensive backs: two cornerbacks and two safeties. Even 22 personnel sends five eligible receivers out on every play, and any of them can run deep, so a defense without two corners and two safeties has nobody left to stop the long pass. Only where there is no room for a long pass, near the goal line or on the shortest yardage, does a defense go below four. That is why the opening play worked the way it did: the offense swapped two receivers for two big men (11 to 22), but the defense, already at five defensive backs, could only come down one, to four. Against 22 in 2025, defenses kept exactly four defensive backs on about three snaps in four, and went to three or fewer only about one in seven, mostly at the goal line or on third- and fourth-and-short.6

Who makes the call? The “single word” from the defensive bench is the name of the package: “Base!” or “Nickel!”, or a team’s own code word for it. Someone on every defensive staff is watching for the offense’s substitutes the moment the whistle blows and calls out the grouping; the defensive play-caller answers with the package, the substitutes run on, and the green dot and sideline signals do the rest. Coordinators talk about wanting to see the offense’s personnel “as early as possible,” and players notice when that relay is slow.7 It is also why the hybrid players above are worth so much to an offense: the caller has to name the grouping in a second or two, and a player who is both a receiver and a back has no right name.

Defenses also substitute by situation, not just by personnel. Most have a third-and-long package (often dime, with a sixth defensive back and extra pass rushers) and a short-yardage or goal-line package (more linemen, fewer defensive backs) that come on at the whistle whatever the offense sends. The offense’s personnel then decides only the fine-tuning.

Show the Python (simulated tracking for the substitution sequence)
# Two groups swap players between plays. gridiron's Play schedules all pre-snap movement to finish at
# the snap, so this sequence is built as BDB-style tracking frames directly (x, y in yards, 10 frames
# per second) and drawn with gridiron's Field and draw_marker (markers a little larger than usual,
# because the whole width of the field has to fit in each small panel).
LOS, BALL_Y = 50.0, FIELD_WIDTH / 2
OFF_BENCH, DEF_BENCH = 25.6, -25.6       # the two benches are on opposite sidelines
HUDDLE_C, HUDDLE_R = (-9.5, 0.0), 2.6

# Offense in 11 personnel (singleback 2x2), then 12 personnel (two tight ends) after the swap.
ALIGN_11 = {"LT": (LINE_D, -3.2), "LG": (LINE_D, -1.6), "C": (LINE_D, 0), "RG": (LINE_D, 1.6),
            "RT": (LINE_D, 3.2), "QB": (UC, 0), "RB": (TB_D, 0), "Y": (LINE_D, TE_W),
            "X": (LINE_D, -15), "H": (OFF_BALL, -9), "Z": (OFF_BALL, 15)}
ALIGN_12 = dict(ALIGN_11, U=(LINE_D, -TE_W), X=(OFF_BALL, -15))
ALIGN_12.pop("H")
OFF_LABEL = {"C": "C", "QB": "Q", "RB": "R", "Y": "Y", "U": "U", "X": "X", "H": "H", "Z": "Z"}
OFF_POS = {"RB": "RB", "Y": "TE", "U": "TE", "X": "WR", "H": "WR", "Z": "WR", "QB": "QB"}

# The defense's nickel (4-2-5) against 11, and its base 4-3 against 12.
NICKEL = {"WDE": (1.0, -4.0), "WDT": (1.0, -0.7), "SDT": (1.0, 2.35), "SDE": (1.0, 5.4),
          "MIKE": (4.5, -1.6), "WILL": (4.5, 2.4), "NB": (5.0, -9.0),
          "LCB": (7.0, -15), "RCB": (7.0, 15), "FS": (12.0, -8.5), "SS": (12.0, 8.5)}
# Base keeps the same four linemen. The new linebacker (S) goes where the nickel back was, onto U,
# the tight end who just came in, so the front sets its strength to the left; M and W stay put.
BASE = {"WDE": (1.0, -4.0), "WDT": (1.0, -0.7), "SDT": (1.0, 2.35), "SDE": (1.0, 5.4),
        "WILL": (4.5, 2.6), "MIKE": (4.5, -1.2), "SAM": (4.0, -6.6),
        "LCB": (7.0, -15), "RCB": (7.0, 15), "FS": (12.5, 1.0), "SS": (8.0, -9.5)}
DEF_LABEL = {"WDE": "E", "SDE": "E", "WDT": "T", "SDT": "T", "WILL": "W", "MIKE": "M", "SAM": "S",
             "NB": "$", "LCB": "C", "RCB": "C", "FS": "FS", "SS": "SS"}

# Huddle: ten players in a ring, the quarterback in the middle facing them.
ring_keys = ["LT", "LG", "H", "C", "RG", "RT", "Y", "Z", "RB", "X"]   # H's spot faces his bench
HUDDLE = {k: (HUDDLE_C[0] + HUDDLE_R * np.cos(a), HUDDLE_C[1] + HUDDLE_R * np.sin(a))
          for k, a in zip(ring_keys, np.linspace(0, 2 * np.pi, 10, endpoint=False) + np.pi / 10)}
HUDDLE["QB"] = HUDDLE_C

# Keyframes: player -> [(t, d, w), ...], t = seconds since the whistle (play clock = 40 - t).
KF = {}
for k in ALIGN_11:                                        # everyone but H: huddle, then break to 12 spots
    if k == "H":
        continue
    KF[k] = [(0, *HUDDLE[k]), (15.5, *HUDDLE[k]), (21.5, *ALIGN_12[k])]
KF["H"] = [(0, *HUDDLE["H"]), (3.0, *HUDDLE["H"]), (8.0, -9.0, OFF_BENCH), (24, -9.0, OFF_BENCH)]
KF["U"] = [(0, -11.5, OFF_BENCH), (3.0, -11.5, OFF_BENCH), (8.2, *HUDDLE["H"]),
           (15.5, *HUDDLE["H"]), (21.5, *ALIGN_12["U"])]
for k, spot in NICKEL.items():                            # defense waits in nickel, then adjusts to 12
    if k == "NB":
        KF[k] = [(0, *spot), (9.0, *spot), (14.0, 3.0, DEF_BENCH), (24, 3.0, DEF_BENCH)]
    else:
        KF[k] = [(0, *spot), (16.0, *spot), (21.5, *BASE[k])]
KF["SAM"] = [(0, 6.0, DEF_BENCH), (9.5, 6.0, DEF_BENCH), (14.5, 4.5, -8.5), (16.0, 4.5, -8.5),
             (21.5, *BASE["SAM"])]
# The umpire stands over the ball while the offense substitutes, then steps back to his spot in the
# offensive backfield (NFL umpires have lined up behind the offense since 2010, except late in halves).
UMP = [(0, -12.5, -5.5), (3.5, -12.5, -5.5), (5.0, -0.6, 0.0), (15.0, -0.6, 0.0), (17.0, -12.0, -5.5)]


def at(kf, t):
    ts = np.array([q[0] for q in kf])
    return (float(np.interp(t, ts, [q[1] for q in kf])), float(np.interp(t, ts, [q[2] for q in kf])))


def sub_tracking(t_end=24.0, dt=0.1):
    """BDB-style tracking frames for the substitution sequence."""
    rows = []
    times = np.round(np.arange(0, t_end + 1e-9, dt), 2)
    for fid, t in enumerate(times, 1):
        for k, kf in KF.items():
            d, w = at(kf, t)
            side = "def" if k in DEF_LABEL else "off"
            lab = DEF_LABEL.get(k) if side == "def" else OFF_LABEL.get(k, "")
            rows.append(dict(frameId=fid, time=float(t), displayName=k, x=LOS + d, y=BALL_Y - w,
                             side=side, label=lab))
        rows.append(dict(frameId=fid, time=float(t), displayName="football", x=LOS, y=BALL_Y,
                         side="ball", label=""))
    df = pd.DataFrame(rows)
    df["_id"] = df["displayName"]
    return df


SUBS = {"H": style.MUTED, "U": TE_RING, "NB": style.MUTED, "SAM": style.DEFENSE}
SUB_NOTES = ["Whistle. 11 personnel in the huddle (H too);\nU and S wait on their sidelines",
             "Offense subs: H (a receiver) jogs off,\nU (a tight end) runs on. Umpire over the ball",
             "Defense matches: the nickel back\nruns off, S (a linebacker) runs on",
             "Both set: 12 personnel against\na base 4-3 (four DBs, three LBs)"]
SUB_TIMES = [1.5, 6.0, 12.5, 23.5]


def sub_panel(ax, df, t, small=True):
    tmap = df.groupby("frameId")["time"].first()
    fid = int((tmap - t).abs().idxmin())
    fld = Field(ax=ax, orient="vertical", los=LOS, window=(-14.5, 14.5), to_go=None,
                numbers=False, hashes=True)
    cur = df[df["frameId"] == fid]
    trails = df[(df["frameId"] <= fid) & df["displayName"].isin(list(SUBS)) & (df["time"] <= 15.5)]
    ppy = fld.pts_per_yard()
    for k, g in trails.groupby("displayName"):
        px, py = fld.xy(g["x"].values, g["y"].values)
        ax.plot(px, py, color=style.OFFENSE if k in OFF_LABEL else style.DEFENSE, lw=0.9, alpha=0.45,
                zorder=3)
    for _, row in cur.iterrows():
        x, y = fld.xy(row["x"], row["y"])
        draw_marker(ax, float(x), float(y), row["side"], row["label"], ppy * (1.5 if small else 1.1),
                    r=0.85 if small else 0.75)
    for k, col in SUBS.items():                    # ring the four players who change
        row = cur[cur["displayName"] == k].iloc[0]
        x, y = fld.xy(row["x"], row["y"])
        ax.add_patch(Circle((float(x), float(y)), 1.4, fc="none", ec=col, lw=1.6, zorder=7))
    ud, uw = at(UMP, t)                            # the umpire: striped disc
    x, y = fld.xy(LOS + ud, BALL_Y - uw)
    ax.add_patch(Circle((float(x), float(y)), 0.9, fc="white", ec=style.INK, lw=0.9, hatch="||||",
                        zorder=9))
    ax.text(float(x) + 1.4, float(y), "ump", fontsize=6.0 if small else 7.5, color=style.INK,
            ha="left", va="center", zorder=9, fontweight="bold")
    for wy, txt in [(OFF_BENCH, "offense's bench"), (DEF_BENCH, "defense's bench")]:
        bx, by = fld.xy(LOS + 12.9, BALL_Y - wy)
        ax.text(float(bx), float(by), txt, fontsize=6.5 if small else 8, color=style.INK_2,
                ha="left" if wy < 0 else "right", va="center", style="italic", zorder=6)
    return fld

Watch it happen. Between plays, the offense swaps a receiver for a second tight end, and the defense swaps its nickel back for a linebacker:

Show the Python
SUB_DF = sub_tracking()
if PDF:
    fig, axes = plt.subplots(2, 2, figsize=(6.6, 5.0))
    for i, (ax, t) in enumerate(zip(axes.flat, SUB_TIMES)):
        sub_panel(ax, SUB_DF, t)
        ax.set_title(f"{i + 1}.  play clock :{40 - t:.0f}", fontsize=8.5, color=style.INK_2, loc="left")
        ax.text(0.5, -0.02, SUB_NOTES[i], transform=ax.transAxes, ha="center", va="top", fontsize=7.3,
                color=style.INK, linespacing=1.15)
    fig.tight_layout(h_pad=2.4, w_pad=0.8)
    plt.show()
else:
    fig, ax = plt.subplots(figsize=(6.4, 3.9))
    tmap = SUB_DF.groupby("frameId")["time"].first()

    def update(fid):
        ax.clear()
        t = float(tmap[fid])
        sub_panel(ax, SUB_DF, t, small=False)
        note = [n for tt, n in zip([0.0, 4.0, 9.5, 20.5], SUB_NOTES) if tt <= t + 1e-6][-1]
        ax.set_title(f"play clock :{40 - t:.0f}    " + note.replace("\n", " "), fontsize=8.5,
                     color=style.INK, loc="left")
        return []

    frames = sorted(SUB_DF["frameId"].unique())[::2]
    anim = animation.FuncAnimation(fig, update, frames=frames, interval=200)
    html_video(anim)
Figure 9.4: Substitution matching, between two plays (simulated, BDB-format tracking; times are typical, not fixed). In this figure the rings mark only the four players who change: gray = leaving, gold = the tight end coming on, orange = the linebacker coming on. 1: the offense huddles in 11 personnel (H is in the huddle) while the defense waits in nickel ($ = nickel back); the two substitutes, U and S, wait on their own sidelines. 2: U runs on for H; the umpire (‘ump’, striped) stands over the ball so the offense can’t snap yet. 3: the defense answers, its nickel back off and a linebacker (S) on. 4: 12 personnel lines up against a base 4-3, with S on U, the tight end who just arrived. The defense is drawn in its alignment for clarity; until the offense breaks the huddle it really stands loose, watching its sideline. Offense: Q = quarterback, R = running back, X/Z = receivers, Y/U = tight ends. Defense: E = end, T = tackle, M/W/S = linebackers, C = cornerback, FS/SS = safeties.

Notice two things. First, the order: the offense moved first and the defense second, and the defense had no sensible way to go first, because any guess could be answered by the next substitute. Second, the defense only matched bodies. It still has no idea whether the second tight end is there to block or to run a route, and the offense hasn’t chosen its formation. The defense has spent its answer; the offense keeps its next two moves.

The rules that protect the match

You met the rule behind panel 2 in What a Play Really Is: when the offense substitutes, the umpire stands over the ball and the snap waits until the defense has had a reasonable chance to respond, except in the last two minutes of each half; when the offense doesn’t substitute, there is no pause, and a defense that changes players anyway risks twelve men on the field.8 Three details matter for personnel:

  • Any substitution starts the clock on that protection, even a like-for-like one. Swap a tired receiver for a fresh receiver and the personnel hasn’t changed, but the umpire still holds the ball. That’s why fast-tempo offenses rotate players only at stoppages: one swap and their tempo is gone.
  • Not substituting is a weapon. If the defense is in base against a 12-personnel offense that is throwing well, the offense can stay in 12, play fast, and keep those linebackers on the field. In-Game Adjustments, Tempo, and the Sideline Battle builds that game out.
  • Bluffing has a limit. An offense can’t run a player on to bait the defense into a change and then pull him straight back off: a substitute who comes on and talks to his teammates must stay for at least one play, or the team is warned and then penalized.9 The fakes come in the formation, not in the substitution.
ImportantWhy it exists

Substitution matching exists because offenses get to choose first. The offense commits to its bodies first, in plain view, and in exchange keeps the last word on the formation and the play. The defense gets the last word on bodies, and the rules guarantee it time to use it. So the offense picks personnel hoping the defense’s best answer to who is the wrong answer to where and what.

What defenses actually do

Here is how NFL defenses answered each grouping in 2025, counting the defensive backs on the field for every regular-season run and pass:

Show the Python (load ten seasons of play-by-play and participation data)
from gridiron.data import nfl, pbp

SEASONS = list(range(2016, 2026))
part = nfl().load_participation(SEASONS).to_pandas()
COLS = ["season", "season_type", "game_id", "play_id", "posteam", "down", "play_type", "pass",
        "wp", "half_seconds_remaining", "shotgun", "ydstogo"]
plays = pbp(SEASONS, columns=COLS)
plays = plays[(plays.season_type == "REG") & plays.play_type.isin(["pass", "run"]) & plays.down.notna()]
df = plays.merge(part, left_on=["game_id", "play_id"], right_on=["nflverse_game_id", "play_id"])


def count(s, positions):
    """Sum the counts for some positions in a personnel string. Two formats exist:
    2016-2022 (NGS) '1 RB, 2 TE, 2 WR' (linemen listed only when there are six or more), and
    2023-2025 (FTN) '1 C, 2 G, 1 QB, 1 RB, 2 T, 2 TE, 2 WR' (every player by roster position)."""
    if not isinstance(s, str):
        return np.nan
    return sum(int(n) for n, pos in re.findall(r"(\d+)\s+([A-Z]+)", s) if pos in positions)


op = df["offense_personnel"]
df["rb"] = op.map(lambda s: count(s, {"RB", "FB"}))       # fullbacks count as backs
df["te"] = op.map(lambda s: count(s, {"TE"}))
df["wr"] = op.map(lambda s: count(s, {"WR"}))
df["ol"] = op.map(lambda s: count(s, {"OL", "C", "G", "T"}))
df.loc[(df.season <= 2022) & (df.ol == 0), "ol"] = 5      # the NGS strings omit a normal five
df["db"] = df["defense_personnel"].map(lambda s: count(s, {"DB", "CB", "S", "FS", "SS"}))
df = df[df.rb.notna() & df.te.notna()].copy()
df["pers"] = df.rb.astype(int).astype(str) + df.te.astype(int).astype(str)
df["jumbo"] = df.ol >= 6

# "Neutral": 1st and 2nd down, win probability 20-80%, outside the last two minutes of each half.
neutral = df[df.down.isin([1, 2]) & df.wp.between(0.2, 0.8) & (df.half_seconds_remaining > 120)]
print(f"{len(df):,} regular-season runs and passes with personnel, 2016-2025")
Show the Python
s = df[(df.season == 2025) & df.db.between(2, 8)].copy()
s["pkg"] = pd.cut(s.db, [0, 3, 4, 5, 9], labels=["3 or fewer DBs", "4 DBs (base)", "5 DBs (nickel)",
                                                  "6+ DBs (dime)"])
order = ["10", "11", "12", "21", "13", "22"]
ct = (pd.crosstab(s.pers, s.pkg, normalize="index") * 100).reindex(order)
n = s.pers.value_counts().reindex(order)
cols = {"3 or fewer DBs": "#898781", "4 DBs (base)": style.DEFENSE, "5 DBs (nickel)": style.OFFENSE,
        "6+ DBs (dime)": style.THIRD}
fig, ax = plt.subplots(figsize=(6.5, 2.6))
ax.set_axisbelow(True)          # gridlines behind the bars, not through the labels
left = np.zeros(len(order))
for c in ["4 DBs (base)", "5 DBs (nickel)", "6+ DBs (dime)", "3 or fewer DBs"]:
    v = ct[c].values
    ax.barh(range(len(order)), v, left=left, color=cols[c], label=c, height=0.66, edgecolor=style.SURFACE)
    for i, (l, val) in enumerate(zip(left, v)):
        if val >= 9:
            ax.text(l + val / 2, i, f"{val:.0f}%", ha="center", va="center", fontsize=7.5,
                    color="white" if c != "3 or fewer DBs" else style.INK)
    left += v
ax.set_yticks(range(len(order)), [f"{o}  (n={n[o]:,})" for o in order], fontsize=8)
ax.invert_yaxis()
ax.set_xlim(0, 100)
ax.set_xlabel("share of the offense's plays (%)", fontsize=8)
ax.grid(axis="y", visible=False)
ax.set_title("Offensive personnel → the defense's answer, 2025", fontsize=10)
ax.legend(ncol=4, fontsize=7, loc="upper center", bbox_to_anchor=(0.45, -0.27), frameon=False)
plt.show()
Figure 9.5: How defenses matched each offensive grouping in 2025 (regular-season runs and passes; nflverse participation data, charted by FTN, a football-data company). Against three or four receivers (11, 10), defenses played nickel or dime almost every time. Against two tight ends or two backs (12, 21), they went to their base defense about 60% of the time; against the heaviest groupings (13, 22), about 80%, sometimes with only three defensive backs (goal-line packages).

The matching is strong but not mechanical. Against 11 personnel, defenses were in nickel or dime about 93% of the time (the slot-receiver problem from The Twenty-Two). Against 12 and 21, though, about a third of defenses stayed in nickel. Those are defenses making a bet: that their nickel back is good enough against the run to survive a second tight end, and that keeping a fifth defensive back is worth it in case the offense throws. The 2025 league as a whole played base on about 30% of snaps, up from a low of about 20% in 2023. The offenses got bigger, so the defenses got bigger too.10

Why personnel is the first clue

The pre-snap read that this course builds, play by play, runs in a fixed order: situation → personnel → formation → motion → the defense’s box and safeties → prediction. (The checklist is assembled step by step through Part 2, and Motion and Shifts as Weapons puts it on one card.) Personnel comes right after the situation for a simple reason: it is the first clue you can see, and you can see it early.

Show the Python
clues = [("Situation (down, distance, score, clock)", 40, 0, style.MUTED),
         ("Personnel (who runs on and off)", 37, 0, style.OFFENSE),
         ("Defense's personnel answer", 33, 0, "#e87ba4"),
         ("Formation (where they line up)", 17, 0, style.OFFENSE_DARK),
         ("Motion and shifts", 7, 0, style.THIRD),
         ("Defense's box and safeties (often disguised)", 5, 0, style.DEFENSE)]
fig, ax = plt.subplots(figsize=(6.5, 2.9))
for i, (name, start, end, col) in enumerate(clues):
    ax.barh(i, start - end, left=end, color=col, height=0.55, alpha=0.9)
ax.set_xlim(41, -0.5)           # the play clock counts down, left to right
ax.set_xticks([40, 30, 20, 15, 10, 5, 0], [":40", ":30", ":20", ":15", ":10", ":05", "snap"], fontsize=8)
ax.set_yticks(range(len(clues)), [c[0] for c in clues], fontsize=8)
ax.tick_params(axis="y", length=0)
ax.invert_yaxis()
ax.axvline(40, color=style.BASELINE, lw=0.8)
ax.text(40, -0.75, "whistle", ha="center", fontsize=7, color=style.INK_2)
ax.axvline(15, color=style.BASELINE, lw=0.8, ls=(0, (3, 2)))
ax.text(15, -0.75, "radio cuts out", ha="center", fontsize=7, color=style.INK_2)
ax.set_xlabel("play clock", fontsize=8)
ax.grid(axis="y", visible=False)
ax.spines["left"].set_visible(False)
plt.show()
Figure 9.6: When each pre-snap clue becomes readable during a typical huddling snap (times vary by team and tempo). The situation is known before the whistle; the offense’s personnel shows as substitutes run on, usually with half a minute or more left on the play clock, and the defense’s answering substitutes follow a few seconds later; the formation appears only when the huddle breaks; motion, the defense’s box and its safeties come last, and the defense works to disguise them.

Personnel does three jobs at once.

It narrows the menu. An offense in 22 can’t throw to four receivers; an offense in 11 can’t line up with a tight end beside each tackle. Every grouping rules out much of the playbook and rules in a few favorites (Classic Formations and Spread Formations pair them up).

It moves the run/pass odds, modestly for the common groupings and a lot for the heavy ones, as the numbers below show.

It sets up the matchups. This is the deepest of the three. Because the defense matches personnel and the offense then picks the formation, every grouping is a bet about what the defense will do with its answer. Twelve personnel shows it best:

Show the Python
fig, axes = plt.subplots(1, 2, figsize=(6.6, 3.95))


def nudge_sde(players, w=4.4):
    """Move the strong-side end to a 6i (inside shoulder of the tight end spot), clear of the tackle's label."""
    return [q.moved(w=w * (1 if q.w > 0 else -1)) if q.key == "SDE" else q for q in players]


# Left: 12 personnel, tight, against nickel. Seal the nickel back and run around him.
off = line5() + [P("QB", "QB", UC, 0, "Q"), P("RB", "RB", TB_D, 0, "R"), P("Y", "TE", LINE_D, TE_W),
                 P("U", "TE", LINE_D, -TE_W), P("X", "WR", OFF_BALL, -10.5), P("Z", "WR", OFF_BALL, 10.5)]
nick = [D("WDE", "DE", 1.0, -5.6, "E"), D("WDT", "DT", 1.0, -0.7, "T"), D("SDT", "DT", 1.0, 2.35, "T"),
        D("SDE", "DE", 1.0, 4.4, "E"), D("WILL", "WILL", 4.5, -2.0), D("MIKE", "MIKE", 4.5, 1.6),
        D("NB", "NB", 3.4, 5.9, "$"), D("LCB", "CB", 6.5, -10.5), D("RCB", "CB", 7.0, 11.5),
        D("FS", "FS", 12.0, -6.0), D("SS", "SS", 12.0, 6.0)]
a = Play(off, nick, los=35, to_go=10)
a.block("Y", target="NB")                 # the tight end seals the nickel back inside
a.block("RT", target="SDE")               # the tackle reaches the end
a.block("RG", target="MIKE")              # the guard climbs to the middle linebacker
a.block("Z", target="RCB")                # the receiver blocks the corner, the force player
a.run("RB", [(-3.5, 2.5), (-0.6, 7.4), (2.6, 8.9), (8.5, 10.0)], absolute=True, delay=0.5)
a.highlight("Y", "U", color=TE_RING)
a.highlight("NB", color=style.DEFENSE)
a.note("Y seals\nthe $ inside", at=(6.4, 4.9))
a.draw(ax=axes[0], lateral=(-17, 17), window=(-9.0, 14.5), title=False)
hide_yard_numbers(axes[0])
axes[0].set_title("Defense in nickel: run at it", fontsize=9.5, loc="left", color=style.INK)

# Right: the same 12 personnel spread out against base, single-high. Throw at the linebacker.
off = line5() + [P("QB", "QB", GUN, 0, "Q"), P("RB", "RB", GUN, -2.2, "R"),
                 P("X", "WR", LINE_D, -15), P("U", "TE", OFF_BALL, -8.5),
                 P("Y", "TE", OFF_BALL, 8.5), P("Z", "WR", LINE_D, 15)]
base = nudge_sde(defense("4-3_over", "single_high", off))
fix = {"SAM": dict(d=4.5, w=8.0), "WILL": dict(d=4.5, w=-4.0), "MIKE": dict(d=5.0, w=0.4),
       "LCB": dict(d=6.5), "RCB": dict(d=6.5), "FS": dict(d=13.0, w=0.0), "SS": dict(d=7.5, w=-9.0)}
base = [q.moved(**fix[q.key]) if q.key in fix else q for q in base]
b = Play(off, base, los=35, to_go=10)
b.route("Y", r.seam(10.5))
b.highlight("Y", "U", color=TE_RING)
b.highlight("SAM", color=style.DEFENSE)
b.note("S must run\nwith Y", at=(8.6, 3.6))
b.draw(ax=axes[1], lateral=(-17, 17), window=(-9.0, 14.5), title=False)
hide_yard_numbers(axes[1])
axes[1].set_title("Defense in base: throw at it", fontsize=9.5, loc="left", color=style.INK)
fig.tight_layout(w_pad=0.6)
plt.show()
Figure 9.7: The two-tight-end dilemma. Left: the defense answered 12 personnel with nickel, so the offense lines up tight and runs at the smallest defender. The tight end (Y, about 250 pounds) seals the nickel back ($, about 195) inside, Z blocks the cornerback, the right guard climbs to the middle linebacker, and the back runs around the sealed nickel (the backside defenders are left to the backside blockers, not drawn). Right: the defense answered with its base 4-3 and a single deep safety, so the offense splits both tight ends out like receivers; with no safety over the seam, a linebacker (S) has to run with a tight end straight up the field (a ‘seam’ route). Gold rings = tight ends.

That’s the bind. If the defense answers two tight ends with nickel, it has a 195-pound defensive back near the line of scrimmage, and the offense can aim its run at him: a 250-pound tight end walls him off, and the back runs around the spot where a linebacker should have been. If it answers with base, it has three linebackers on the field, and the offense can spread the tight ends out and make a linebacker cover one of them down the field. The better the tight ends are at both jobs, the worse the defense’s choice gets. That’s why great two-way tight ends are so valuable, and it’s a big part of why 12 personnel has grown: an offense with two of them can pick whichever mismatch the defense hands it, after the defense has made its choice.

The same logic runs the other way for the defense. A defense with a safety who can tackle like a linebacker, or a linebacker who can cover like a safety, doesn’t have to choose: it can stay in one grouping against everything and take the matchup game away. That’s the idea behind the big nickel, a nickel whose fifth defensive back is a bigger safety rather than a small slot corner, and behind hybrid defenders in general, the defensive mirror of a Deebo Samuel. The 2025 Seahawks, for example, played 220-pound rookie safety Nick Emmanwori as a nickel defender.11 A First Look at Defense tells that story.

Personnel and run/pass: what the numbers say

So how much does personnel tell you about run or pass? Here is every grouping with at least 300 plays in the three most recent seasons, counting only neutral situations: first and second down, outside the last two minutes of each half, with neither team far ahead or behind (win probability between 20% and 80%). Think of neutral as “not late, not lopsided.” In those situations, the offense’s choice reflects its plan rather than the scoreboard; Tendencies: PROE, Personnel, Motion, and Coverage defines it formally and builds on it.

Show the Python
s = neutral[neutral.season.isin([2023, 2024, 2025])]
g = s.groupby("pers")["pass"].agg(["mean", "size"])
g = g[g["size"] >= 300]
j = s[s.jumbo]["pass"].agg(["mean", "size"])
g.loc["jumbo"] = [j["mean"], j["size"]]
g = g.sort_values("mean")
league = s["pass"].mean()
fig, ax = plt.subplots(figsize=(6.5, 3.2))
ax.set_axisbelow(True)          # gridlines behind the bars
colors = [style.DEFENSE if k == "jumbo" else style.OFFENSE for k in g.index]
ax.barh(range(len(g)), g["mean"] * 100, color=colors, height=0.62)
for i, (k, row) in enumerate(g.iterrows()):      # value labels inside the bar ends, clear of the line
    ax.text(row["mean"] * 100 - 1, i, f"{row['mean'] * 100:.0f}%", va="center", ha="right", fontsize=8,
            color="white", fontweight="bold")
ax.axvline(league * 100, color=style.INK_2, lw=1, ls=(0, (4, 3)), zorder=0.5)
ax.text(league * 100 + 0.8, 0, f"all plays\n{league * 100:.0f}%", fontsize=7.5, color=style.INK_2,
        va="center", ha="left")
ax.set_yticks(range(len(g)), [f"{k}  (n={int(row['size']):,})" for k, row in g.iterrows()], fontsize=8)
ax.set_xlim(0, 75)
ax.set_xlabel("share of plays that were dropbacks (%)", fontsize=8)
ax.grid(axis="y", visible=False)
ax.set_title("Pass rate by personnel, neutral situations, 2023–2025", fontsize=10)
plt.show()
Figure 9.8: Pass rate by personnel in neutral situations (1st and 2nd down, win probability 20–80%, outside the last two minutes of each half), 2023–2025 regular seasons, groupings with at least 300 plays. A pass is any dropback, including sacks and scrambles. Jumbo plays (six or more offensive linemen) are also counted in the grouping their backs and tight ends give them. The dashed line is the league rate in the same situations.

Three things jump out.

The common groupings are weak clues. In neutral situations, offenses passed on about 56% of plays from 11 personnel and about 49% from 12. That’s a nudge, not a signal. If you predicted “pass” every time you saw 11 and “run” every time you saw 12, you’d be right about 54% of the time on those plays, barely better than the 53% you’d get by saying “pass” on every one of them. That’s deliberate: the groupings an offense uses most are the ones it has built to do both. A team whose 11 personnel was 80% pass would be giving away the game. Even four receivers (10 personnel) was only about 55% pass: spreading the defense out also thins the number of defenders near the ball, which makes running easier.

The heavy groupings are stronger clues. From 22 personnel, offenses passed only about 36% of the time, and from jumbo about 30%. That is also where the rare pass is most dangerous, because the defense has loaded up for the run: the logic of play-action (faking a handoff before throwing).

Personnel predicts the formation as well as the play. From 11 personnel, offenses lined up in the shotgun on about 70% of plays; from 13, only about 30%. Heavy personnel usually means the quarterback under center, close to the ball. You can use that as a check on your count: if you counted three tight ends and the quarterback is five yards deep in the shotgun, count again, or expect something unusual.

How much does personnel add?

Here is a blunt test. Take every neutral play from 2023 to 2025 and make the simplest guess the situation allows: for each down and distance (first or second down; 1–3, 4–6, 7–10 or 11+ yards to go), say whichever of run or pass is more common there. That guesser is right about 60% of the time. Now let it see the personnel as well. It is still right about 60% of the time. On 11 and 12, which are more than eight snaps in ten, the more common call simply doesn’t change. Let it see one more pre-snap fact, whether the quarterback is in the shotgun, and it jumps to about 66%.

Show the Python (how much does personnel add to a down-and-distance guess?)
def hit_rate(d, keys):
    """Guess the more common call (run or pass) in each cell of `keys`; return the share guessed right."""
    p_pass = d.groupby(keys, observed=True)["pass"].transform("mean")
    return ((p_pass >= 0.5) == (d["pass"] == 1)).mean()


def with_dist(d):
    return d.assign(dist=pd.cut(d.ydstogo, [0, 3, 6, 10, 100], labels=["1-3", "4-6", "7-10", "11+"]))


recent = with_dist(neutral[neutral.season.isin([2023, 2024, 2025])])
every = with_dist(df[df.season.isin([2023, 2024, 2025])])
HIT = {"neutral, down & distance": hit_rate(recent, ["down", "dist"]),
       "neutral, + personnel": hit_rate(recent, ["down", "dist", "pers"]),
       "neutral, + personnel + shotgun": hit_rate(recent, ["down", "dist", "pers", "shotgun"]),
       "all downs, down & distance": hit_rate(every, ["down", "dist"]),
       "all downs, + personnel": hit_rate(every, ["down", "dist", "pers"])}
print({k: round(v, 3) for k, v in HIT.items()})
assert 0.58 < HIT["neutral, down & distance"] < 0.62 and abs(HIT["neutral, + personnel"] - 0.60) < 0.02
assert 0.64 < HIT["neutral, + personnel + shotgun"] < 0.67
assert 0.61 < HIT["all downs, down & distance"] < 0.64 and 0.66 < HIT["all downs, + personnel"] < 0.69
short = every[every.down.isin([3, 4]) & (every.ydstogo <= 1)]
SHORT = short.groupby("pers")["pass"].mean()
assert SHORT["22"] < 0.2 and 0.28 < SHORT["11"] < 0.36

So on ordinary first and second downs, personnel barely changes a simple run/pass guess. Its worth is elsewhere: it tells you which formations are coming (and so where the quarterback will stand), it sets up the matchups in the dilemma above, and in the situations where heavy groupings appear, it means a lot. Count every down, not just neutral ones, and adding personnel lifts the same guesser from about 63% to about 67%. The opening play’s situation is the clearest case: on third- or fourth-and-1 from 2023 to 2025, offenses in 22 personnel threw only about 15% of the time, against about 32% from 11.

CautionCommon misconception

“Heavy personnel means run.” Only the heaviest groupings lean hard. Two tight ends threw on about half of neutral plays, because players who can block and catch hide the offense’s intentions, which is exactly why offenses have moved toward them. The formation, the motion and the defense’s answer tell you more, and the next five chapters add those clues one at a time.

Personnel by season: the rise of 11, and the 2025 turn

Three wide receivers weren’t always normal. Football Outsiders, charting games by hand, counted 11 personnel on about 40% of NFL plays in 2011, 46% in 2012 and 51% in 2013.12 That climb, driven by more passing, by college spread offenses supplying receivers and quarterbacks raised on three-receiver sets, and by defenses answering with more defensive backs, finished just before the data in this chapter begins. Different charters count slightly differently, so those numbers don’t belong on the same axis as the ones below, but the direction is clear. Here’s how the mix moved from 2016 to 2025.

Show the Python
share = (pd.crosstab(df.season, df.pers, normalize="index") * 100)
lines = {"11": style.OFFENSE, "12": style.DEFENSE, "21": style.THIRD, "13": "#eda100"}
fig, ax = plt.subplots(figsize=(6.5, 3.1))
other = 100 - share[list(lines)].sum(axis=1)
ends = {k: share.loc[2025, k] for k in lines}
ends["all other"] = other.loc[2025]
# end-of-line labels, nudged apart so close values (13, 21, other) don't collide
ys = dict(sorted(ends.items(), key=lambda kv: kv[1]))
pos, last = {}, -99.0
for k, v in ys.items():
    pos[k] = max(v, last + 3.6)
    last = pos[k]
for k, col in lines.items():
    ax.plot(share.index, share[k], color=col, lw=2.2, marker="o", ms=3.5)
ax.plot(other.index, other, color=style.MUTED, lw=1.4, ls=(0, (3, 2)))
for k, v in ends.items():
    ax.text(2025.3, pos[k], f"{k}: {v:.1f}%", va="center", fontsize=8, fontweight="bold",
            color=lines.get(k, style.INK_2))      # each end label in its line's color
ax.axvline(2022.5, color=style.BASELINE, lw=1, ls=(0, (1, 2)))
ax.text(2022.55, 46, "source change\n(NGS → FTN)", fontsize=7, color=style.INK_2, va="center", ha="left")
ax.set_xlim(2015.6, 2027.6)
ax.set_xticks(range(2016, 2026), [str(y) for y in range(2016, 2026)], fontsize=8)
ax.set_ylim(0, 70)
ax.set_ylabel("share of plays (%)", fontsize=8)
ax.set_title("Personnel share by season", fontsize=10)
plt.show()
Figure 9.9: Share of NFL regular-season runs and passes by offensive personnel, 2016–2025 (course calculation from nflverse participation data). 11 personnel held at roughly 60% for a decade and fell to 58.8% in 2025, its lowest in the period; 12 personnel rose to 24.4%, its highest. The dotted line marks a change of data source (see ‘Where the numbers come from’ below).

For most of the decade the story is stability: 11 personnel on about 60% of plays, 12 on about 20%, 21 on about 8%, everything else sharing the rest. By the late 2010s, three receivers was simply what an offense looked like, and Sean McVay’s Rams were the extreme case of that habit.

Then 2025 moved. Eleven personnel fell to 58.8%, its lowest share of the decade, and 12 rose to 24.4%, its highest. Thirteen personnel jumped from 3.7% to 5.1%. Next Gen Stats, measuring it its own way, found offenses with fewer than three wide receivers on 41.7% of 2025 plays, the first season at or above 38% since it began tracking in 2016, and the share kept climbing as the season went on.13

Why then? No rule change or single coach set it off, and nobody can prove a cause from a chart. The explanation most analysts give is an arms race. Through the late 2010s and early 2020s, defenses got smaller and faster to stop the pass: more nickel and dime, and more looks with two safeties deep (which A First Look at Defense introduces as two-high). A defense like that has fewer big bodies near the line, a “light box,” and it was winning: getting to quarterbacks faster and covering better. Offenses answered by getting bigger, to force defenses into base, where “they generally do not want an undersized extra DB to get steamrolled by a tight end,” as an NFL.com Next Gen Stats analysis put it, and then running at whatever was left, often with the quarterback back under center.14 And as you saw above, defenses answered that by playing more base in 2025. Each side’s move invites the other’s. The Modern Era tells the whole arms race, and Working with nflverse Data in Python shows you how to rebuild these charts yourself.

NoteGo deeper: where the numbers come from

Every chart in this chapter is a course calculation from the nflverse participation data (load_participation()), which lists the personnel on each play, joined to nflverse play-by-play.15 Three cautions if you reproduce them. First, the source changes between 2022 and 2023: Next Gen Stats supplied 2016–2022 and FTN Data supplies 2023 onward, and the personnel strings change format (“1 RB, 2 TE, 2 WR” before, every player by position after), so the code normalizes them before counting; the personnel shares agree across the break. Second, players are counted by roster position, not by role. A snap with a second quarterback or a defensive player in the offense (a string like “2 QB, 1 RB, 1 TE, 2 WR”) is labeled by its backs and tight ends, here “11,” even though only two receivers are out there. That is about 1% of plays, too few to move the charts, and the course keeps the simple backs-then-tight-ends count so its numbers match the course fact sheet. Third, the participation file for a season arrives only after the season ends, so 2026 numbers won’t exist until early 2027. Published series from other providers (Next Gen Stats, Sharp Football, PFF) count slightly differently; never put two providers’ numbers on one axis.

Film room

Six teams, six ways of using personnel. Each shows a different idea from this chapter.

Show the Python
teams = [("LA", 2018, "Rams 2018"), ("SF", 2019, "49ers 2019"), ("BAL", 2019, "Ravens 2019"),
         ("PHI", 2024, "Eagles 2024"), ("LA", 2025, "Rams 2025"), ("SEA", 2025, "Seahawks 2025"),
         (None, 2025, "League 2025")]
groups = ["11", "12", "21", "13", "22"]
gcol = {"11": style.OFFENSE, "12": style.DEFENSE, "21": style.THIRD, "13": "#eda100", "22": "#e87ba4",
        "other": "#c3c2b7"}
rows = []
for tm, yr, name in teams:
    t = df[(df.season == yr) & ((df.posteam == tm) if tm else True)]
    vc = t.pers.value_counts(normalize=True) * 100
    row = {k: vc.get(k, 0.0) for k in groups}
    row["other"] = 100 - sum(row.values())
    rows.append((name, row))
fig, ax = plt.subplots(figsize=(6.5, 3.4))
ax.set_axisbelow(True)          # gridlines behind the bars
for i, (name, row) in enumerate(rows):
    left = 0
    for k in groups + ["other"]:
        v = row[k]
        ax.barh(i, v, left=left, color=gcol[k], height=0.66, edgecolor=style.SURFACE,
                label=k if i == 0 else None)
        if v >= 7:
            ax.text(left + v / 2, i, f"{v:.0f}", ha="center", va="center", fontsize=7.5,
                    color="white" if k not in ("other", "13") else style.INK)
        left += v
ax.set_yticks(range(len(rows)), [n for n, _ in rows], fontsize=8)
ax.invert_yaxis()
ax.set_xlim(0, 100)
ax.set_xlabel("share of the team's plays (%)", fontsize=8)
ax.grid(axis="y", visible=False)
ax.legend(ncol=6, fontsize=7.5, loc="upper center", bbox_to_anchor=(0.45, -0.2), frameon=False,
          title=None, handlelength=1.2)
ax.set_title("Personnel fingerprints", fontsize=10)
plt.show()
Figure 9.10: Personnel fingerprints: each team’s share of regular-season runs and passes by grouping, with the 2025 league for comparison (course calculation from nflverse participation data). The 2018 Rams lived in 11; the 2019 49ers led the league in 21; the 2019 Ravens used 22 personnel more than five times as often as the league; the 2024 Eagles leaned on 12; the 2025 Rams split their snaps between 11 and 13, and the 2025 Seahawks spread theirs across four groupings.
NoteFilm room: Los Angeles Rams 2017–2019

When the Rams hired Sean McVay in January 2017, he was 30, the youngest head coach in modern NFL history.16 His offense became famous for using one grouping, 11 personnel, almost all the time: about 81% of plays in 2017, 93% in 2018 and 73% in 2019, against a league norm of about 60%.

Why would a coach give up the personnel game entirely? Because he was playing a different one. If the offense never substitutes, the defense never gets a reason to, so it sits in nickel all day: in 2018, defenses had five or more defensive backs on about 94% of the Rams’ plays. That suited McVay twice over. A nickel defense has one fewer big body near the line, so his running game was always aimed at a lighter front. And a grouping that never changes tells the defense nothing: in neutral situations in 2018 the Rams passed on 56% of their 11-personnel plays, almost exactly the league rate. McVay put his variety into formation, motion and play-action instead, and it worked: the 2018 Rams reached Super Bowl LIII.

What to notice: an offense that never substitutes isn’t being lazy. It’s denying the defense the first clue.

NoteFilm room: San Francisco 49ers, 21 personnel and Kyle Juszczyk

Kyle Shanahan’s 49ers have been the league’s leading 21-personnel team for most of his tenure: first in the league in 21 personnel in 2017, 2018, 2019, 2021, 2024 and 2025, and second in the other seasons from 2017 to 2025, typically around a third of their plays, against a league share of about 8%. The player who makes it work is fullback Kyle Juszczyk, who signed in March 2017 on a contract that made him, at the time, the highest-paid fullback in the league.17

Juszczyk can block a linebacker on a run, line up in the slot, or split out wide like a receiver. When he lines up wide, the defense that matched 21 personnel with its base package has to send someone with him: a cornerback, which leaves a linebacker covering a receiver somewhere else, or a linebacker, which takes a defender away from the ball (Drill 2 below draws exactly this). Two backs also give the offense two running threats for every play-action fake.

What to notice: when the 49ers come out with a fullback, find him after the huddle breaks. Where he lines up tells you which matchup they’re hunting.

NoteFilm room: Baltimore Ravens 2019

The 2019 Ravens, with Lamar Jackson at quarterback (that season’s unanimous MVP) and Greg Roman as offensive coordinator, set the NFL record for team rushing yards in a season, 3,296.18 They did it big. Baltimore used 22 personnel on about 16% of its plays, more than five times the league’s 3%, with tight ends Mark Andrews, Hayden Hurst and Nick Boyle and the two-way lineman/fullback Patrick Ricard.

The surprise is in the tendencies. In neutral situations, the Ravens ran on about 64% of their 11-personnel plays (the league passed 56% of the time) and passed on about 63% of their 12-personnel plays (league: 49%). With the most dangerous runner on the field at quarterback, three receivers didn’t mean “pass” and two tight ends didn’t mean “run.”

What to notice: league tendencies are where you start. A good scouting report, like your own prediction log, adjusts them team by team (The Ravens’ Option Run Game has the full case).

NoteFilm room: Philadelphia Eagles 2024

The 2024 Eagles signed Saquon Barkley, who ran for 2,005 yards, the ninth 2,000-yard season in NFL history, and won Super Bowl LIX, 40–22 over Kansas City.19 Their personnel was the vehicle: 12 personnel on about a third of their plays (the league was at 23.5%), with tight end Dallas Goedert and a second tight end next to the best offensive line in football.

What to notice: in neutral situations the Eagles passed on only 32% of their 12-personnel plays, against 48% for the league. Everyone knew 12 meant run, and they ran anyway. Predictability costs less when you’re good enough at the thing you’re predicted to do (The Eagles: Trenches, RPOs, and the Tush Push).

NoteFilm room: the 2025 turn, Rams and Seahawks

Two NFC West teams show two different routes to the 2025 turn. McVay’s Rams, the 11-personnel team of 2018, used 13 personnel on about 31% of their plays, far more than any other team (the next was 13%), and 12 on only about 9%: they were a binary team, three receivers or three tight ends. Matthew Stafford won the MVP.20 Defenses did match: they played base on about three of every four Rams 13 snaps. But matching bought them little, because the big grouping didn’t mean run. In neutral situations the Rams threw on 47% of their 13-personnel plays, the league’s 13 rate, almost always from under center, where a pass looks like a run until the fake. The tell was the light grouping: from 11 they threw 67% of the time, against a league 57%.

The champion Seattle Seahawks, under offensive coordinator Klint Kubiak in 2025, got big the other way: by variety. They used fewer than three wide receivers on about 55% of their snaps, fifth-most in the league, spread across 11, 12, 21 and 22, with a fullback (usually Robbie Ouzts) on the field for about 19% of their plays, on the way to beating New England 29–13 in Super Bowl LX.

What to notice: when the Rams show three tight ends, don’t assume run; when they show three receivers, lean pass. When Seattle subs, the fullback is the tell to watch for.

Watch for it on Sunday

TipWatch for it: call the personnel
  • Between plays, watch the sidelines, not the replay. When the broadcast shows a wide shot, count who jogs off and who runs on. Two receivers off and a tight end and a fullback on is a change from 11 to 22.
  • Count backs, then tight ends, and say the number out loud. Backs stand behind or beside the quarterback; tight ends are the big players at the end of the line or just behind it, usually with a hand down. Receivers are what’s left: 5 minus the other two (4 minus, if there’s a sixth lineman).
  • Look for a sixth lineman. A 300-pounder standing where a tight end usually stands means jumbo. Listen for the referee’s “reporting as eligible.”
  • Then watch the defense. Did a defensive back run off or a linebacker run on? Base, nickel or dime? If the offense substituted and the defense didn’t, the defense is making a bet.
  • Make your first guess from the numbers (neutral downs, 2023–2025): 11 about 56% pass, 12 about 49%, 13 about 45%, 22 about 36%, jumbo about 30%. Treat 11 and 12 as coin flips with a lean; treat 22 and jumbo, especially on third-and-short, as run unless something says otherwise. Then adjust for down and distance, where the quarterback stands, and what you know about the team.
  • Write it down. Add a “personnel” column to your prediction log: grouping, your guess, the result. After a half, compare your hit rate on the heavy groupings with your hit rate on 11.

Predict the play

TipPredict the play 1: count it

First-and-10 at your own 30. The offense has substituted and is lined up. The broadcast’s roster graphic gives you only sizes, not positions: #87 6-5, 258 lb · #81 6-4, 250 lb · #88 6-6, 262 lb · #26 5-11, 215 lb · #11 6-1, 195 lb. On the close-up, #87 and #88 each have a hand on the ground; #81 and #11 are standing up. (A made-up roster for practice. The quarterback is Q, in the pistol, about three and a half yards behind the center; the linemen are unlabeled.)

Show the Python
off = line5() + [P("QB", "QB", PISTOL, 0, "Q"), P("RB", "RB", TB_D, 0, "26"),
                 P("Y", "TE", LINE_D, TE_W, "87"), P("U", "TE", LINE_D, -15.0, "81"),
                 P("H", "TE", OFF_BALL, -WING_W - 0.4, "88"), P("X", "WR", OFF_BALL, 15.0, "11")]
q = Play(off, None, los=40, to_go=10)
q.draw(lateral=(-19.5, 19.5), window=(-9.5, 3.5), title=False)
hide_yard_numbers(plt.gca())
plt.show()
Figure 9.11: Drill 1: who is on the field? Players are labeled by jersey number, as you’d see them on TV. #81 stands far out on the left, at the end of the line; #88 is a yard off the line just outside the left tackle; #87 is next to the right tackle; #11 is wide on the right; #26 is behind the quarterback, who is in the pistol.

What’s the personnel, which defensive package should you expect, and does the grouping lean run or pass?

13 personnel. The weights do the work. #87, #81 and #88 are all 250 pounds or more: tight-end bodies, and the two with a hand down are standing where tight ends stand. #26, at 215 and behind the quarterback, is the back. #11, at 195, is the only receiver. One back, three tight ends: 13. It doesn’t matter that #81 is standing out wide where a receiver usually lines up; he is a 250-pound tight end wherever he stands.

The defense: against 13 personnel in 2025, defenses played their base package (four defensive backs) about 80% of the time, and went to nickel only about 13%. Expect linebackers.

Run or pass: on neutral downs, 13 personnel leaned run, with passes on about 45% of plays. Now look at #81. A base defense has two cornerbacks, and one of them will probably go out with him. That corner is fine in coverage, but on a run he is a 195-pound defender being blocked by a 250-pound tight end, and he is the man who is supposed to turn the run back inside. If the defense sends a linebacker or safety instead, it has one fewer player near the ball. Either way the offense has made the defense pay for its answer, the same bind as the two-tight-end dilemma. Personnel gives you the first guess; the formation sharpens it, which is where the next chapters go.

TipPredict the play 2: the fullback goes wide

First-and-10 at midfield. The offense came on with a fullback (F), a tailback (R), one tight end and two receivers. The defense answered with its base 4-3. Then the offense lined up like this:

Show the Python
off = line5() + [P("QB", "QB", GUN, 0, "Q"), P("RB", "RB", GUN, -2.2, "R"), P("Y", "TE", LINE_D, TE_W),
                 P("X", "WR", LINE_D, -15), P("Z", "WR", OFF_BALL, 9.5), P("FB", "FB", OFF_BALL, 15.5, "F")]
dfn = nudge_sde(defense("4-3_over", "two_high", off))
fix = {"SAM": dict(d=4.5, w=9.0), "RCB": dict(d=6.0, w=15.5), "LCB": dict(d=6.5), "SDE": dict(d=1.4),
       "SS": dict(d=11.0, w=9.5), "FS": dict(d=12.0, w=-7.0), "MIKE": dict(d=4.5, w=1.2),
       "WILL": dict(d=4.5, w=-2.6)}
dfn = [q2.moved(**fix[q2.key]) if q2.key in fix else q2 for q2 in dfn]
q = Play(off, dfn, los=60, to_go=10)
ring_by_body(q)
q.draw(lateral=(-19, 19), window=(-7.5, 14), title=False)
hide_yard_numbers(plt.gca())
plt.show()
Figure 9.12: Drill 2: the fullback (F, aqua ring) has split out wide on the right, outside receiver Z. The defense is in its base 4-3: E = end, T = tackle, S/M/W = linebackers, C = cornerback, FS/SS = safeties. Gold ring = tight end.

What’s the personnel? Who has the advantage now, and what is the offense hoping for?

Still 21 personnel, even with the fullback out wide.

Who has the advantage: the offense. Look where the defense’s players have had to go. A cornerback went out with the fullback, so the Sam linebacker (S) has had to walk out over the slot receiver (Z): a bigger, slower player than a nickel back, now covering a receiver. If the defense swaps those two (corner on Z, linebacker on the fullback), it has a linebacker 15 yards from the ball instead. Either way, a linebacker has left the area near the ball.

What the offense is hoping for: whichever of two good options the defense leaves. With a linebacker on Z, a quick throw to Z. With that linebacker gone from the middle, a run: there are only six defenders near the line (four linemen and the two remaining linebackers) against six blockers (five linemen and the tight end), a fair fight for the tailback.

The defense has one more answer: the strong safety walks down over Z, so the Sam linebacker can stay in the box. That keeps the Sam near the ball for the run but leaves only one safety deep, which is exactly the picture the offense’s play-action is built to beat. Every answer costs something. This is the 49ers’ 21-personnel idea in one picture: the defense answered who, and the offense answered where.

TipPredict the play 3: third-and-7

Third-and-7 at the opponent’s 45. On first and second down the offense ran twice from 12 personnel (two tight ends), and the defense matched with its base 4-3 both times. Second down ends, and fourteen seconds later the ball is snapped from this picture:

Show the Python
off = line5() + [P("QB", "QB", GUN, 0, "Q"), P("RB", "RB", GUN, -2.2, "R"), P("X", "WR", LINE_D, -15),
                 P("Y", "TE", OFF_BALL, 5.6), P("U", "TE", OFF_BALL, 9.8), P("Z", "WR", LINE_D, 15)]
dfn = defense("4-3_over", "two_high", off)
fix = {"SAM": dict(d=4.5, w=7.6), "MIKE": dict(d=5.0, w=2.0), "WILL": dict(d=4.8, w=-3.4),
       "SS": dict(d=10.0, w=10.0), "FS": dict(d=13.0, w=-2.0), "LCB": dict(d=6.5), "RCB": dict(d=6.5)}
dfn = [q2.moved(**fix[q2.key]) if q2.key in fix else q2 for q2 in dfn]
q = Play(off, dfn, los=65, to_go=7)
ring_by_body(q)
q.draw(lateral=(-19, 19), window=(-7.5, 15), title=False)
hide_yard_numbers(plt.gca())
plt.show()
Figure 9.13: Drill 3: third-and-7, fourteen seconds after the last whistle. Gold rings = tight ends; S, M and W are linebackers; E = end, T = tackle, C = cornerback, FS/SS = safeties.

Count both teams’ personnel. What’s wrong for the defense, why didn’t it fix it, and what does the offense probably want?

What’s wrong: nothing changed. The offense is still in 12 personnel (two gold rings), and the defense is still in base, with three linebackers, on third-and-7, a passing down. Fourteen seconds is a no-huddle snap: the offense didn’t substitute.

Why the defense didn’t fix it: it couldn’t, safely. With no offensive substitution the umpire had no reason to stand over the ball. A defense that starts running a linebacker off and a defensive back on while the offense is set risks twelve men on the field at the snap, five yards on third-and-7.

What the offense wants: a pass, probably to a tight end. The offense spent two plays teaching the defense that this grouping runs, which kept it in base. Now both tight ends stand off the line, split out beside Z, and linebackers have to cover them away from the ball.

If the defense saw this coming, its answer was the situational substitution from earlier in the chapter: send its third-down package on at the whistle, before the offense hurried to the line, and live with the risk of a run against a lighter defense.

Takeaways

  • Personnel is two digits: running backs, then tight ends. Receivers fill the rest of the five changeable spots (5 − backs − tight ends), or four of them in jumbo, where a sixth lineman takes one.
  • Personnel counts who is on the field, not where they stand, and the gap between personnel and formation is where offenses build mismatches. Hybrid players blur the count on purpose.
  • The offense commits first, and the defense matches in direction (more receivers, more defensive backs; more tight ends and backs, more linebackers) down to a floor of four defensive backs. The rules give it time whenever the offense substitutes, and none when it doesn’t.
  • Personnel shifts the odds more than it settles them. On ordinary first and second downs it barely improves a down-and-distance guess; its weight shows in heavy groupings and short yardage, in the formation it predicts, and in the matchups it sets up.
  • The league’s mix moves with the defense. 11 personnel rose through the early 2010s, held near 60% for a decade, then fell to a decade low in 2025 as offenses went bigger against lighter defenses, and defenses answered with more base.

Connections

Builds on: The Twenty-Two (who the running backs, tight ends and receivers are, and why each body type does its job), What a Play Really Is (the forty seconds in which personnel goes on first and the play call follows), and How to Watch a Broadcast Like You Mean It (your prediction log, which now gets a personnel column).

Sets up: Formation Rules and the Language of Alignment (the seven-on-the-line rule, the X/Y/Z/H letters and reporting eligible), Classic Formations and Spread Formations (which formations go with which personnel), A First Look at Defense (base, nickel and dime properly), and Motion and Shifts as Weapons (the next clue after formation). Further on: Red Zone, Goal Line, and Short Yardage (heavy personnel where it matters most), In-Game Adjustments, Tempo, and the Sideline Battle (tempo against the substitution rule), Tendencies: PROE, Personnel, Motion, and Coverage (neutral situations and team tendency reports), and Building a Roster (why the personnel you want decides the players you pay).

Case studies: the Shanahan tree, the Ravens with Lamar Jackson, the Eagles’ trenches and the Lions under Ben Johnson.

NoteGo deeper
  • The nflverse participation data dictionary, for the personnel fields and their caveats: https://nflreadr.nflverse.com/articles/dictionary_participation.html.
  • Sharp Football Analysis publishes team personnel usage and success rates by grouping each season (https://www.sharpfootballstats.com); note that its counts differ slightly from nflverse’s.
  • Pat Kirwan, Take Your Eye Off the Ball (2010; updated as Take Your Eye Off the Ball 2.0, 2015): a former NFL coach and scout teaches fans to read personnel and formations before the snap.
  • Chris B. Brown, The Art of Smart Football (2015): essays on how offenses build mismatches out of personnel and formation.

  1. John Harris, “Football 101 | Daily Brew,” HoustonTexans.com (July 5, 2023), on two-back groupings called “20 Rabbit” and “20 Pony”: https://www.houstontexans.com/news/football-101-daily-brew . Word names are team-specific; the digits are the common language.↩︎

  2. Course calculation from nflverse participation data, regular-season runs and passes: 10 personnel was 0.5–2.9% of plays each season from 2016 to 2025 (0.8% in 2025), 20 personnel 0.5–1.5%, and 00 personnel 0.2% or less.↩︎

  3. Deebo Samuel’s 2021 regular season with San Francisco: 59 carries, 365 rushing yards, 8 rushing touchdowns (course calculation from nflverse play-by-play; also Pro Football Reference: https://www.pro-football-reference.com/players/S/SamuDe00.htm). Taysom Hill’s and Patrick Ricard’s listed positions are from the nflverse participation data (offense_positions, from FTN): Hill QB on 530 plays in 2023, TE on 270 in 2024, QB on 261 in 2025; Ricard FB in all three seasons.↩︎

  4. NFL owners approved expanded jersey numbers in April 2021 (running backs, tight ends and receivers may wear 1–49 and 80–89; Sports Illustrated, Apr. 8, 2021: https://www.si.com/nfl/2021/04/08/nfl-allow-jersey-number-changes-2021-season) and allowed No. 0 for all non-linemen in 2023 (NFL Football Operations, “NFL Jersey Numbers”: https://operations.nfl.com/the-rules/rules-changes/nfl-jersey-numbers).↩︎

  5. Detroit at Dallas, Dec. 30, 2023 (Week 17), Cowboys 20, Lions 19. After the game referee Brad Allen said in a pool report that No. 70 (Skipper), not No. 68 (Decker), had reported as eligible; see “2023 Detroit Lions season,” Wikipedia: https://en.wikipedia.org/wiki/2023_Detroit_Lions_season and Associated Press, “Lions take issue with officials after potential winning 2-point pass is negated by penalty” (Dec. 31, 2023; the try came with 23 seconds to go, and after the penalty Detroit’s second try was intercepted but nullified by a Dallas offside, and its third fell incomplete): https://www.ksat.com/sports/2023/12/31/lions-take-issue-with-officials-after-potential-winning-2-point-pass-is-negated-by-penalty/ . Detroit’s sixth-lineman share by season is a course calculation from nflverse participation data (9.8% of runs and passes in 2022, second to Cleveland; 5.1% in 2023; 6.4% in 2024).↩︎

  6. Course calculation from nflverse participation data (defensive backs counted from defense_personnel). Against 22 personnel in 2025: four defensive backs on 76% of plays, five on 10%, three or fewer on 14%. Of the 2023–2025 plays on which a defense had three or fewer defensive backs against 22, 58% came with two yards or less to go and 44% inside the opponent’s 5-yard line.↩︎

  7. Pittsburgh’s 2026 defensive coordinator Patrick Graham, who calls the defense from the coaches’ booth, “likes to be able to see the offensive personnel grouping as early as possible before the snap” (Brian Batko, “Why Graham prefers a bird’s eye view,” Steelers.com, Aug. 22, 2026): https://www.steelers.com/news/why-graham-prefers-a-bird-s-eye-view . In 2015, Bills linebacker Preston Brown described problems with “the personnel coming in and out” and the sideline card system the team had used to receive personnel groupings (ESPN via ABC7, Dec. 22, 2015): https://abc7ny.com/post/preston-brown-on-delayed-calls-from-coaches-shouldnt-happen/1132194/↩︎

  8. NFL Rulebook, Rule 5, Section 2 (substitutes): when the offense substitutes outside the last two minutes of a half, the umpire stands over the ball until the referee judges the defense has had a reasonable chance to respond: https://operations.nfl.com/the-rules/nfl-rulebook/ ; Football Zebras, “NFL officials instructed on new procedures for substitution tactics” (Oct. 2, 2025): https://www.footballzebras.com/2025/10/nfl-officials-instructed-on-new-procedures-for-substitution-tactics/ (the umpire moves off the ball when the referee ends the substitution period, and the ball may be snapped once he is at least as deep as the deepest offensive player). Umpires have lined up in the offensive backfield since 2010, returning to the middle of the defense only late in halves and near the goal line: Associated Press, “NFL again tweaks positioning of umpires” (Sept. 7, 2010): https://www.washingtontimes.com/news/2010/sep/7/nfl-again-tweaks-positioning-of-umpires/↩︎

  9. NFL Rule 5-2-5(b): a player who approaches the huddle and communicates with a teammate must take part in at least one play; since 2017 the first violation draws a warning and later ones a 15-yard unsportsmanlike-conduct penalty. Football Zebras, “Rarely penalized substitution call will now come after a warning” (July 2017): https://www.footballzebras.com/2017/07/rarely-penalized-substitution-call-will-now-come-after-a-warning/↩︎

  10. The 2025 percentages in the chart are a course calculation from nflverse participation data (defensive backs counted from defense_personnel). The league-wide base rates are from NFL Next Gen Stats: base defense on 29.7% of snaps in 2025, up from a low of 20.5% in 2023 (James Reber, NFL.com, “NFL trend watch: heavy personnel could shape Super Bowl LX showdown between Patriots and Seahawks”): https://www.nfl.com/news/nfl-trend-watch-heavy-personnel-could-shape-super-bowl-lx-showdown-between-patriots-and-seahawks . The nflverse count gives a similar path (21.8% base in 2023, 30.9% in 2025).↩︎

  11. James Reber, NFL.com, “NFL trend watch: heavy personnel could shape Super Bowl LX showdown between Patriots and Seahawks,” which notes Seattle’s use of rookie safety Nick Emmanwori (6-foot-3, 220 pounds) as a nickel defender against heavy personnel: https://www.nfl.com/news/nfl-trend-watch-heavy-personnel-could-shape-super-bowl-lx-showdown-between-patriots-and-seahawks↩︎

  12. Aaron Schatz, Football Outsiders, as reported by Larry Hartstein, “Three-receiver formations standard in today’s NFL,” CBS Sports (May 25, 2014): 11 personnel on 40.4% of plays in 2011, 45.7% in 2012 and 51.2% in 2013: https://www.cbssports.com/fantasy/football/news/three-receiver-formations-standard-in-todays-nfl . Football Outsiders charted by hand and counted somewhat differently from the nflverse data used in this chapter.↩︎

  13. James Reber, NFL.com, same article: fewer than three wide receivers on 41.7% of 2025 plays, the first season at or above 38% since NGS began tracking in 2016, rising from 38.8% in Weeks 1–9 to 44.5% from Week 10 on. The 11, 12 and 13 shares are a course calculation from nflverse participation data (see the next note); the course’s own count of fewer-than-three-receiver plays in 2025 also comes to 41.7%.↩︎

  14. James Reber, NFL.com (same article as the note on Emmanwori): offenses saw defenders “pressuring their quarterbacks more quickly, gang tackling their rushers earlier and becoming more effective in coverage,” and answered by getting bigger; “The idea of using heavier personnel is to force defenses into base (four defensive backs), as they generally do not want an undersized extra DB to get steamrolled by a tight end or offensive lineman in run defense.”↩︎

  15. nflverse, load_participation() data dictionary: https://nflreadr.nflverse.com/articles/dictionary_participation.html ; Next Gen Stats supplies 2016–2022 and FTN Data (CC-BY-SA 4.0) 2023 onward. Method for every chart: regular-season plays with play_type of pass or run and a recorded down; backs = RB + FB; jumbo = six or more offensive linemen (OL, or C + G + T from 2023). Pass = any dropback, including sacks and scrambles. Data downloaded through the course’s gridiron.data cache.↩︎

  16. “Sean McVay,” Wikipedia (hired by the Rams on Jan. 12, 2017, at 30, the youngest head coach in modern NFL history): https://en.wikipedia.org/wiki/Sean_McVay ; Sports Illustrated, Jan. 12, 2017: https://www.si.com/nfl/2017/01/12/sean-mcvay-hired-los-angeles-rams-head-coach ; Pro Football Hall of Fame, “Youngest NFL Coaches (Modern Era)” (30 years, 353 days): https://profootballhof.com/football-history/youngest-nfl-coaches-modern-era . Rams personnel shares, the 2018 neutral pass rate (56% from 11 personnel, league 56%) and the defenses the Rams faced in 2018 (five or more defensive backs on 93.6% of plays) are course calculations from nflverse data. The Rams lost Super Bowl LIII to New England, 13–3.↩︎

  17. Kyle Juszczyk signed with San Francisco as a free agent in March 2017 on a four-year, $21 million contract, the largest for a fullback at the time: https://en.wikipedia.org/wiki/Kyle_Juszczyk ; CBS Sacramento, “49ers To Sign FB Kyle Juszczyk To 4-Year Deal” (March 2017): https://www.cbsnews.com/sacramento/news/49ers-to-sign-fb-kyle-juszczyk/ . San Francisco released him and re-signed him on a two-year deal in March 2025, so he played the 2025 season with the 49ers (ESPN): https://www.espn.com/nfl/story/_/id/44261853/source-49ers-bring-back-fb-kyle-juszczyk-two-year-deal . The 49ers’ 21-personnel ranks (first in 2017, 2018, 2019, 2021, 2024 and 2025; second in 2020, 2022 and 2023, behind New England in 2020 and Miami in 2022–2023) are a course calculation from nflverse participation data.↩︎

  18. Pro Football Reference, 2019 Baltimore Ravens (14–2; 3,296 rushing yards, an NFL single-season team record): https://www.pro-football-reference.com/teams/rav/2019.htm ; Jackson was the unanimous 2019 MVP: “2019 Baltimore Ravens season,” Wikipedia: https://en.wikipedia.org/wiki/2019_Baltimore_Ravens_season . The Ravens’ personnel shares and neutral-situation pass rates (11 personnel 36% pass, 12 personnel 63%; league 56% and 49%) are course calculations from nflverse data.↩︎

  19. Pro Football Reference, Saquon Barkley (2,005 rushing yards in 2024): https://www.pro-football-reference.com/players/B/BarkSa00.htm ; Super Bowl LIX result from the course fact sheet (Eagles 40, Chiefs 22; Feb. 9, 2025). Barkley was the ninth player to rush for 2,000 yards in a season (NFL.com): https://www.nfl.com/news/saquon-barkley-becomes-ninth-player-to-rush-for-2000-yards-in-a-season . Eagles personnel shares and neutral pass rates (12 personnel 32% pass; league 48%) are course calculations from nflverse data.↩︎

  20. Course calculation from nflverse participation data, 2025 regular season: Rams 11 personnel 59.7%, 13 personnel 31.3%, 12 personnel 8.9% (next-highest 13-personnel team: Pittsburgh, 13.4%); defenses had four defensive backs on 73% of Rams 13-personnel plays; Rams neutral-situation pass rates 46.5% from 13 (n = 159; league 45.8%) and 67.1% from 11 (n = 255; league 56.7%); the Rams were in the shotgun on 9% of 13-personnel plays. Seahawks: fewer than three wide receivers on 55.2% of plays; a fullback on 18.9% (FTN lists Robbie Ouzts as the fullback on 145 of those plays, Brady Russell on 45). Stafford’s MVP and the Super Bowl LX result (Seahawks 29, Patriots 13) and Seattle’s 2025 offensive coordinator (Klint Kubiak) are from the course fact sheet (AP awards, Feb. 5, 2026; Super Bowl LX, Feb. 8, 2026).↩︎