Appearance
Off Ball Screens
The off_ball_screens section in the markings response contains a row for each off ball screen we identify in the tracking data.
Overview
An off ball screen occurs when an offensive player attempts to set a screen for a player who is not the ball handler.
Each row includes:
- The key players involved (screener, cutter, and their defenders)
- A start/end window around the screen action
- The single frame where we consider the screen to have been closest to being set (
screening_he_frame) - Additional detailed data about the screening action
A few important notes to be aware of:
- Off ball screens are difficult to have both high precision (not having many incorrect labels) and high recall (not missing many true labels) because there is so much off ball movement that can look like a screening situation and there are many screening situations that might look a lot like normal off ball movement. If you want to include most true off ball screens, you end up getting a lot of noise. In this data set we have prioritized precision over recall, i.e. we try not to include off ball screens that are not true off ball screens if possible, even if that means missing a good chunk of true off ball screens.
- We currently only include screens that occur when the screener is in the frontcourt and not further than 40 feet up the court than the ball. So this means that at this point, backcourt off ball screens or screens way up the court before the ball is in range are not included.
- We also do not currently purposefully include players screening their own matchup. Ideally we would include them, but it is difficult to intentionally get those at this point without introducing a lot of noise. Some of these still do get included in the data set if the matchup flipped quickly and our system doesn't view the two players as matched up at some point during the screening action. But otherwise these situations will not be included in this data set.
- If a screener goes to set a screen and the cutter does not use the screen, we aim to capture it in the data set but our hit rate on those is much lower. For example, here Duop Reath goes to set a screen in the right corner that is not used:
- Sideline and baseline out of bounds situations present some weird situations, where a cutter might be moving past another player but that player is not obviously setting a screen, like Hauser here: In these cases, if it seems pretty clear that this is a screen-like action, we aim to include them as off ball screens even though a screen isn't truly set. But because of the lack of clear screening intent, these have a higher miss rate.
- Similarly, there are cases where players are in a stack and cut off each other, for example, McCain curling off Yabusele here: Again, these often function like off ball screens and so we aim to include them even if the screener isn't truly setting a screen, but can have a higher miss rate because they don't look like classic screens.
- Cases with clusters of players together or ambiguous matchup situations (for example when someone receives an off ball screen but then immediately turns and sets one for the same player) are quite difficult to handle properly. We sometimes properly include these in the data set but do not get the player being screened or the cutter correct. For example, in this situation, because of the way the players get clustered and defend the play, our model gets a bit confused. We currently include an off ball screen, but it has Duren screening Sarr:
- One of our bigger sources of error is activity with a player in the dunker spot. There are cases where a player will be in the dunker and pin in a helping defender to open up a shooter in the corner, and cases where they are simply cutting down to the dunker, and both look very similar. For example, compare this action with Tobias Harris attempting to screen Trae Young (which we'd ideally want to count as an off ball screen): With this action where Brandon Clarke cuts down to the dunker and Anfernee Simons has to navigate around him (which we'd ideally not want to count as an off ball screen):
Sample Response
json
{
"markings": {
"off_ball_screens": [
{
"attacking_positive_x_basket": true,
"ballhandler_outcome": "shot",
"ballhandler_possession_touch_id": "possession_touch_fde352bc48b83cf13ba16b20f128f7d6",
"ballhandler_possession_touch_outcome_id": "shot_828453dbae99ddf663dbb1c7f9ac9f22",
"chance_id_ctg": "chance_6dda4f792e347155bb8d20ab207d75bd",
"cutter_id_nba": 1642843,
"cutter_loc_x": 184.46,
"cutter_loc_y": -115.87,
"drive_id": "drive_3481dfdc115e5b401fe2293886fe11e4",
"end_game_clock": 378.0,
"end_he_frame": 42966,
"end_shot_clock": 17.0,
"end_wall_clock": "2025-12-23T01:22:30.573+00:00",
"event_frame_number_he": null,
"event_type": null,
"game_id_nba": "0022500404",
"gap_defender_id": null,
"high_weak_side_defender_id": null,
"is_direct": false,
"led_to_event": false,
"low_man_id": null,
"off_ball_screen_id_ctg": "off_ball_screen_407744444fc99966190ef8ea252749e5",
"pbp_event_id": null,
"period": 1,
"possession_id_ctg": "possession_6dda4f792e347155bb8d20ab207d75bd",
"screened_defender_id_nba": 1630529,
"screened_defender_loc_x": 270.42,
"screened_defender_loc_y": -30.04,
"screener_id_nba": 203076,
"screener_loc_x": 270.62,
"screener_loc_y": 20.1,
"screening_game_clock": 378.0,
"screening_he_frame": 42941,
"screening_shot_clock": 18.0,
"screening_wall_clock": "2025-12-23T01:22:30.156+00:00",
"start_game_clock": 379.0,
"start_he_frame": 42886,
"start_shot_clock": 18.0,
"start_wall_clock": "2025-12-23T01:22:29.24+00:00"
}
]
}
}Fields
Identifiers
off_ball_screen_id_ctg
Type: string
CTG-generated unique identifier for this off-ball screen event
game_id_nba
Type: string
NBA game ID
chance_id_ctg
Type: string | Nullable
CTG-generated identifier linking this off-ball screen to the chance in which it occurred.
possession_id_ctg
Type: string | Nullable
CTG-generated identifier linking this off-ball screen to the possession in which it occurred.
Players
screener_id_nba
Type: integer
NBA player ID of the offensive player setting the screen
cutter_id_nba
Type: integer
NBA player ID of the offensive player using the off-ball screen
screened_defender_id_nba
Type: integer
NBA player ID of the defender who is being screened
Location
attacking_positive_x_basket
Type: boolean
true if the offensive team is attacking the basket on the positive-x side of the court (i.e., the basket with positive x coordinates)
screener_loc_x
Type: float | Unit: inches
X coordinate of the screener's location at screening_he_frame
screener_loc_y
Type: float | Unit: inches
Y coordinate of the screener's location at screening_he_frame
cutter_loc_x
Type: float | Unit: inches
X coordinate of the cutter's location at screening_he_frame
cutter_loc_y
Type: float | Unit: inches
Y coordinate of the cutter's location at screening_he_frame
screened_defender_loc_x
Type: float | Unit: inches
X coordinate of the screened defender's location at screening_he_frame
screened_defender_loc_y
Type: float | Unit: inches
Y coordinate of the screened defender's location at screening_he_frame
Timing
period
Type: integer
Period number (1–4 for regulation, 5+ for overtime)
start_he_frame
Type: integer
Hawk-Eye frame number where the screen window begins.
The screen window is the contiguous frame span around screening_he_frame where:
- The screener is relatively close their screening frame location
- The screener is somewhat close to the screened defender
- It is within 3 seconds of the screening frame
end_he_frame
Type: integer
Hawk-Eye frame number where the screen window ends (the last frame in the same window described in start_he_frame).
screening_he_frame
Type: integer
Hawk-Eye frame number that is our best guess for when the screen was initially set.
screening_game_clock
Type: float | Unit: seconds
Game clock (time remaining in the period) at screening_he_frame
screening_shot_clock
Type: float | Unit: seconds
Shot clock at screening_he_frame
screening_wall_clock
Type: string
UTC wall clock timestamp for screening_he_frame (ISO-8601)
start_game_clock
Type: float | Unit: seconds
Game clock (time remaining in the period) at start_he_frame
end_game_clock
Type: float | Unit: seconds
Game clock (time remaining in the period) at end_he_frame
start_shot_clock
Type: float | Unit: seconds
Shot clock at start_he_frame
end_shot_clock
Type: float | Unit: seconds
Shot clock at end_he_frame
start_wall_clock
Type: string
UTC wall clock timestamp for start_he_frame (ISO-8601)
end_wall_clock
Type: string
UTC wall clock timestamp for end_he_frame (ISO-8601)
is_direct
Type: boolean
true when the off-ball screen results in a direct scoring outcome for the cutter: the cutter shoots, is fouled and goes to the free throw line, or turns the ball over — or the cutter makes a final pass and the receiver shoots within one dribble of catching it (with no further passes before the shot). false otherwise.
led_to_event
Type: boolean
true when this off ball screen is deemed to have led to the chance ending event, such as a shot, turnover, foul, kicked ball, or jump ball. We say a off ball screen leads to an event when we think the screen was the main factor in the chance ending event: the defense doesn't reset after and the offense doesn't try to run a new action after the off ball screen.
event_type
Type: string | Nullable
The type of play by play event attributed to this off ball screen. This is null when led_to_event is false.
pbp_event_id
Type: integer | Nullable
Play-by-play event ID for the attributed play by play event. This is null when no play-by-play event is linked.
event_frame_number_he
Type: integer | Nullable
Hawk-Eye frame number for the attributed play by play event. This is null when led_to_event is false.
ballhandler_outcome
Type: string | Nullable
How the cutter's linked possession touch ended after the off-ball screen. One of: shot, pass, turnover, foul, violation, stoppage, unknown. null if the cutter did not receive the ball after the screen.
ballhandler_possession_touch_id
Type: string | Nullable
CTG-generated possession touch ID for the cutter touch linked to this off-ball screen. null if the cutter did not receive the ball after the screen.
ballhandler_possession_touch_outcome_id
Type: string | Nullable
CTG-generated event ID for how the linked cutter possession touch ended. This can be null when the outcome does not have a specific CTG event ID.
drive_id
Type: string | Nullable
CTG-generated drive ID for the earliest cutter drive that overlaps the linked possession touch after the screening frame. null if no such drive is identified.
Off-ball context fields
gap_defender_id
Type: integer | Nullable
NBA player ID of the defender who is on the strong side, is guarding a player who is one pass away, and is not guarding a player in the corner. null if no such defender is identified on this play.
high_weak_side_defender_id
Type: integer | Nullable
NBA player ID of the defender identified as the highest weak-side defender when more than one weak-side defender is present. null if no such defender is identified on this play.
low_man_id
Type: integer | Nullable
NBA player ID of the defender estimated to have low-man responsibility at off-ball screen start (typically the lowest weak-side defender, or the lowest strong-side defender when there are no weak-side defenders). null if no such defender is identified on this play.
