Play by Play entry tool prototype

Home for all your discussion of basketball statistical analysis.
Post Reply
kpascual
Posts: 50
Joined: Thu Mar 01, 2012 7:02 pm

Play by Play entry tool prototype

Post by kpascual »

I've been prototyping a play by play data entry tool, primarily because I thought we haven't been capturing enough useful data (publicly) to do more interesting kinds of data analysis. I couldn't find many existing tools, so I selfishly created one for myself. I would LOVE feedback, because I have no idea if I'm reinventing the wheel.

It turns out that tracking a game is pretty hard in real time. Things move VERY fast, and you often miss data points along the way. I tried using an interface with a bunch of buttons for play types and for players, but that proved too hard, because you had to move your eyes down to the screen, and away from the action.

Because of this, I designed the workflow around a single text box... kind of like a command line. You type text as you go, and the app will parse what you think it means. Typing proves better because you don't have to take your eyes off the action. This all assumes you're a competent typer.

http://vorped.com/wbball/index.php/temp/playbyplay

And here's a sample game (D-league finals game 2):
http://www.youtube.com/watch?v=oyKBI5ZntRk

Some features:
- Shorthand to describe events faster (i.e. type 'reb' for 'defensive rebound', 'oreb' for offensive rebound)
- Scores update based on the plays you've entered
- Tells you which team currently has the ball, based on the last play
- Relatedly, there's an auto-guess for each play that guesses which team had the ball
- Update prior plays by editing that play's text

Current limitations (because it's a prototype)
- Player parsing doesn't exist yet
- I haven't figured out fouls yet
- Team parsing is still kinda wonky, esp. on prior play updates
- No game times
- No shot charts (yet... I have the code, I haven't figured out the workflow)

List of shorthand plays (regex is what you type, name is what it resolves to):

Code: Select all

                // Canonical play-by-play types
                {regex:'oreb',name: 'offensive rebound',points:0,pc:0},
                {regex:'reb', name: 'defensive rebound',points:0,pc:1},
                {regex:'made 2', name: 'two pointer made',points:2,pc:1},
                {regex:'made 3', name: 'three pointer made',points:3,pc:1},
                {regex:'miss 2', name: 'two pointer missed',points:0,pc:0},
                {regex:'miss 3', name: 'three pointer missed',points:0,pc:0},
                {regex:'made2', name: 'two pointer made',points:2,pc:1},
                {regex:'made3', name: 'three pointer made',points:3,pc:1},
                {regex:'miss2', name: 'two pointer missed',points:0,pc:0},
                {regex:'miss3', name: 'three pointer missed',points:0,pc:0},
                {regex:'missed 2', name: 'two pointer missed',points:0,pc:0},
                {regex:'missed 3', name: 'three pointer missed',points:0,pc:0},
                {regex:'2 missed', name: 'two pointer missed',points:0,pc:0},
                {regex:'3 missed', name: 'three pointer missed',points:0,pc:0},
                {regex:'2 miss', name: 'two pointer missed',points:0,pc:0},
                {regex:'3 miss', name: 'three pointer missed',points:0,pc:0},
                {regex:'2miss', name: 'two pointer missed',points:0,pc:0},
                {regex:'3miss', name: 'three pointer missed',points:0,pc:0},
                {regex:'2 made', name: 'two pointer made',points:2,pc:1},
                {regex:'3 made', name: 'three pointer made',points:3,pc:1},
                {regex:'2made', name: 'two pointer made',points:2,pc:1},
                {regex:'3made', name: 'three pointer made',points:3,pc:1},
                {regex:'ft made', name: 'free throw made',points:1,pc:0},
                {regex:'ft miss', name: 'free throw miss',points:0,pc:0},
                {regex:'madeft', name: 'free throw made',points:1,pc:0},
                {regex:'missft', name: 'free throw miss',points:0,pc:0},
                {regex:'ftmade', name: 'free throw made',points:1,pc:0},
                {regex:'ftmiss', name: 'free throw miss',points:0,pc:0},
                {regex:'foul', name: 'foul',points:0,pc:0},
                {regex:'stl', name: 'steal',points:0,pc:1},
                {regex:'tov', name: 'turnover',points:0,pc:1},
                {regex:'to', name: 'timeout',points:0,pc:0},
                {regex:'24s', name: '24 second violation',points:0,pc:1},
                {regex:'8s', name: '8 second violation',points:0,pc:1},
                {regex:'5s', name: '5 second violation',points:0,pc:1},
                // Special play-by-play types
                // Defensive schemes
                {regex:'23z', name: '2-3 zone',points:0,pc:0},
                {regex:'32z', name: '3-2 zone',points:0,pc:0},
                {regex:'131z', name: '1-3-1 zone',points:0,pc:0},
                {regex:'m2m', name: 'man to man',points:0,pc:0},
                {regex:'box1', name: 'box and 1',points:0,pc:0},
                {regex:'3/4press', name: 'three quarter court press',points:0,pc:0},
                {regex:'fullpress', name: 'full court press',points:0,pc:0},
                // Offensive schemes
                {regex:'isopost', name: 'isolation in post',points:0,pc:0},
                {regex:'isowing', name: 'isolation on wing',points:0,pc:0},
                {regex:'pnr', name: 'pick and roll',points:0,pc:0},
                {regex:'pnp', name: 'pick and pop',points:0,pc:0},
                {regex:'horns', name: 'horns',points:0,pc:0},
Gaines
Posts: 2
Joined: Tue Sep 10, 2013 6:06 am

Re: Play by Play entry tool prototype

Post by Gaines »

I stumbled upon your site and have to say it is quite impressive. Have you read Basketball on Paper by Dean Oliver before? It covers this pretty well and I love your idea of setting it up like this. You could include which zone the player shot from at the end of the line. If you haven't read the book, I would be more than happy to email you a photo of an example of how he does this (granted its on paper, but I think it could be done better on the computer due to typing speeds vs hand writing speeds).
kpascual
Posts: 50
Joined: Thu Mar 01, 2012 7:02 pm

Re: Play by Play entry tool prototype

Post by kpascual »

Gaines wrote:I stumbled upon your site and have to say it is quite impressive. Have you read Basketball on Paper by Dean Oliver before? It covers this pretty well and I love your idea of setting it up like this. You could include which zone the player shot from at the end of the line. If you haven't read the book, I would be more than happy to email you a photo of an example of how he does this (granted its on paper, but I think it could be done better on the computer due to typing speeds vs hand writing speeds).
Yup. I have Basketball on Paper. I wanted to think a bit outside the current basketball dogma and apply current technologies to the problem. But your point is well-taken, the Dean Oliver way is compact and information-dense. I am a little worried that it would be too labor-intensive for the average basketball fan, and am wondering if there's a less-labor-intensive method that supplies most of the useful analytical value.
Gaines
Posts: 2
Joined: Tue Sep 10, 2013 6:06 am

Re: Play by Play entry tool prototype

Post by Gaines »

Oh I understand now, I didn't know that you were making it for the casual fan. It is a great idea, you do some great work regardless.
Youltaithe
Posts: 3
Joined: Tue Nov 12, 2024 3:54 pm

Re: Play by Play entry tool prototype

Post by Youltaithe »

kpascual wrote: Wed Sep 04, 2013 4:15 pm I've been prototyping a play by play data entry tool, primarily because I thought we haven't been capturing enough useful data (publicly) to do more interesting kinds of data analysis. I couldn't find many existing tools, so I selfishly created one for myself. I would LOVE feedback, because I have no idea if I'm reinventing the wheel.

It turns out that tracking a game is pretty hard in real time. Things move VERY fast, and you often miss data points along the way. I tried using an interface with a bunch of buttons for play types and for players, but that proved too hard, because you had to move your eyes down to the screen, and away from the action.

Because of this, I designed the workflow around a single text box... kind of like a command line. You type text as you go, and the app will parse what you think it means. Typing proves better because you don't have to take your eyes off the action. This all assumes you're a competent typer. It turns out that tracking a game in real time is extremely hard. Things move fast, and button-heavy interfaces force you to constantly look away from the action. That’s why I think your “single text box” approach is actually the strongest idea here. It reminds me of how a lot of modern productivity systems evolved — fewer UI clicks, more natural input, faster workflows. Even business automation tools are moving in this direction now, where companies integrate zendesk with other systems through lightweight workflows instead of navigating giant admin panels all day.

http://vorped.com/wbball/index.php/temp/playbyplay

And here's a sample game (D-league finals game 2):
http://www.youtube.com/watch?v=oyKBI5ZntRk

Some features:
- Shorthand to describe events faster (i.e. type 'reb' for 'defensive rebound', 'oreb' for offensive rebound)
- Scores update based on the plays you've entered
- Tells you which team currently has the ball, based on the last play
- Relatedly, there's an auto-guess for each play that guesses which team had the ball
- Update prior plays by editing that play's text

Current limitations (because it's a prototype)
- Player parsing doesn't exist yet
- I haven't figured out fouls yet
- Team parsing is still kinda wonky, esp. on prior play updates
- No game times
- No shot charts (yet... I have the code, I haven't figured out the workflow)

List of shorthand plays (regex is what you type, name is what it resolves to):

Code: Select all

                // Canonical play-by-play types
                {regex:'oreb',name: 'offensive rebound',points:0,pc:0},
                {regex:'reb', name: 'defensive rebound',points:0,pc:1},
                {regex:'made 2', name: 'two pointer made',points:2,pc:1},
                {regex:'made 3', name: 'three pointer made',points:3,pc:1},
                {regex:'miss 2', name: 'two pointer missed',points:0,pc:0},
                {regex:'miss 3', name: 'three pointer missed',points:0,pc:0},
                {regex:'made2', name: 'two pointer made',points:2,pc:1},
                {regex:'made3', name: 'three pointer made',points:3,pc:1},
                {regex:'miss2', name: 'two pointer missed',points:0,pc:0},
                {regex:'miss3', name: 'three pointer missed',points:0,pc:0},
                {regex:'missed 2', name: 'two pointer missed',points:0,pc:0},
                {regex:'missed 3', name: 'three pointer missed',points:0,pc:0},
                {regex:'2 missed', name: 'two pointer missed',points:0,pc:0},
                {regex:'3 missed', name: 'three pointer missed',points:0,pc:0},
                {regex:'2 miss', name: 'two pointer missed',points:0,pc:0},
                {regex:'3 miss', name: 'three pointer missed',points:0,pc:0},
                {regex:'2miss', name: 'two pointer missed',points:0,pc:0},
                {regex:'3miss', name: 'three pointer missed',points:0,pc:0},
                {regex:'2 made', name: 'two pointer made',points:2,pc:1},
                {regex:'3 made', name: 'three pointer made',points:3,pc:1},
                {regex:'2made', name: 'two pointer made',points:2,pc:1},
                {regex:'3made', name: 'three pointer made',points:3,pc:1},
                {regex:'ft made', name: 'free throw made',points:1,pc:0},
                {regex:'ft miss', name: 'free throw miss',points:0,pc:0},
                {regex:'madeft', name: 'free throw made',points:1,pc:0},
                {regex:'missft', name: 'free throw miss',points:0,pc:0},
                {regex:'ftmade', name: 'free throw made',points:1,pc:0},
                {regex:'ftmiss', name: 'free throw miss',points:0,pc:0},
                {regex:'foul', name: 'foul',points:0,pc:0},
                {regex:'stl', name: 'steal',points:0,pc:1},
                {regex:'tov', name: 'turnover',points:0,pc:1},
                {regex:'to', name: 'timeout',points:0,pc:0},
                {regex:'24s', name: '24 second violation',points:0,pc:1},
                {regex:'8s', name: '8 second violation',points:0,pc:1},
                {regex:'5s', name: '5 second violation',points:0,pc:1},
                // Special play-by-play types
                // Defensive schemes
                {regex:'23z', name: '2-3 zone',points:0,pc:0},
                {regex:'32z', name: '3-2 zone',points:0,pc:0},
                {regex:'131z', name: '1-3-1 zone',points:0,pc:0},
                {regex:'m2m', name: 'man to man',points:0,pc:0},
                {regex:'box1', name: 'box and 1',points:0,pc:0},
                {regex:'3/4press', name: 'three quarter court press',points:0,pc:0},
                {regex:'fullpress', name: 'full court press',points:0,pc:0},
                // Offensive schemes
                {regex:'isopost', name: 'isolation in post',points:0,pc:0},
                {regex:'isowing', name: 'isolation on wing',points:0,pc:0},
                {regex:'pnr', name: 'pick and roll',points:0,pc:0},
                {regex:'pnp', name: 'pick and pop',points:0,pc:0},
                {regex:'horns', name: 'horns',points:0,pc:0},
Honestly, I think the “single text box” approach is the smartest part of the whole design. Most live tracking systems fail because they overload the user with UI elements and require too much mouse interaction. Your command-line style workflow makes sense because typing becomes muscle memory after a while, especially for experienced stat keepers.

You’re also solving a real problem: balancing speed vs data richness. Traditional play-by-play logs usually capture only the minimum needed for box scores, while your system is trying to capture tactical context too (defensive schemes, PNRs, presses, etc.), which is where the interesting analysis starts.

A few thoughts:

The shorthand parsing is actually pretty intuitive already. Stuff like made3, oreb, pnr feels natural.
Auto-possession tracking is a huge feature. That alone removes a lot of mental overhead.
Editing previous plays inline is very important because real-time entry always involves mistakes.

I think the biggest challenge long term is ambiguity. Human shorthand becomes inconsistent under pressure. For example:

“miss3”
“3miss”
“bricked 3”
“deep miss”

You’re already compensating for this with regex aliases, which is probably the correct direction.

One thing that might help: separate “event parsing” from “semantic interpretation.” Let the parser accept messy human input first, then normalize later. That gives users freedom while still producing structured data.

Also, I would strongly recommend timestamps as early as possible, even rough ones. Possession-level timing unlocks a huge amount of advanced analysis later.

And honestly, I don’t think you’re reinventing the wheel in a bad way. Existing systems tend to optimize for official stat crews, not for flexible analytical data collection by individuals. Your approach feels closer to a developer tool or power-user workflow, which is actually pretty refreshing.
Crow
Posts: 11370
Joined: Thu Apr 14, 2011 11:10 pm

Re: Play by Play entry tool prototype

Post by Crow »

The site appears down and the Twitter handle in kpascal's profile hasn't posted for 13 years. Nothing here for 12+.

But you could try to reach out directly if you want.
Post Reply