Self-hosted service
paaatrick/playball avatar
paaatrick/playball

playball: Watching Baseball From a Terminal Window

Watch MLB games from the comfort of your own terminal

1,198 stars55 forksJavaScriptMIT

At a glance

What is it?
playball is a Node command line program that renders MLB schedule, standings and live play-by-play in the terminal, with a configurable colour scheme, favourite teams, a World Baseball Classic mode and a Docker image for machines without Node installed. It is a React application drawn with blessed.
Who is it for?
playball fits a person who wants to keep an eye on a game without opening a browser, especially someone on a server or in a terminal multiplexer, and it does not fit a viewer who wants the video, since it is a text rendering of the game state rather than a stream.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 19 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A Ballgame in a Terminal Window

The premise is short: watch MLB games from the comfort of your terminal, and the reason given is that MLB Gameday and MLB.tv are great, but sometimes you want to keep an eye on a game a bit more discreetly. The application puts the game in a terminal window.

The interface is a scoreboard and a play-by-play, not a video player. The top row of the game view shows the count as dots, with the balls, the strikes and the outs, and the bases as diamonds, and the list of plays below carries the detail of what happened on each pitch. The colour scheme for each of those elements is configurable separately, which is the sign of a tool built by someone who reads a lot of baseball on a screen they chose.

The screen layout is built with blessed, a terminal user interface library, with React on top of it through react-blessed. The dependency list shows the state handling too, with Redux and react-redux and the Redux Toolkit, and the data comes from axios, with a winston logger and an update-notifier that checks whether a newer version of the package has been published.

So it is a text-mode application, and the information it carries is the game state, the score and the sequence of plays.

Three Ways to Run It

The install options are given as three tiers, and the first one is unusually frictionless.

If you just want to try it out, the quick start is a single command with no install step:

shell
npx playball

For regular use, the package is installed globally and then run:

shell
npm install -g playball
shell
playball

And if you do not have Node.js on the machine at all, there is a container image:

shell
docker run -it --rm paaatrick0/playball

The container is worth a note, because of a detail that only shows up there. When running via Docker, times will be shown by default in Eastern Time, and the fix is the TZ environment variable, for example:

shell
docker run -it --rm -e TZ=America/Chicago paaatrick0/playball

The same pattern applies for Mountain Time with America/Denver and Pacific Time with America/Los_Angeles, and the README points at the TZ database time zone list for anything else. The default is Eastern because that is the schedule's own timezone, and the container has no local configuration to infer it from.

The Key Map: Schedule, Standings and the Game View

Three global keys move between the views. q quits, c goes to the schedule view and s goes to the standings view.

In the schedule view the arrow keys and the vim style j, k, h and l change the highlighted game, enter opens the highlighted game, p shows the previous day's schedule and results, n shows the next day's schedule, and t returns to today's schedule.

The day navigation is what makes the schedule view more than a list of today's games, because a schedule is a document you consult before and after the games in it rather than only during them.

In the game view the up and down arrows and j and k scroll the list of all plays, which is the live feed. There is no separate refresh key, which suggests the play list updates on its own as the game progresses.

The overall shape is a small state machine: a global level with three views, a schedule view with a selection and a date cursor, and a game view with a scrolling list. It is a design that a terminal user can learn in a minute, which is a reasonable target for a tool meant to be glanced at while doing something else.

Configuration Lives in a Key Value Store

Configuration is handled by a config subcommand, and the interface is the same in all four cases.

Running the subcommand with no additional arguments lists the current values, and the README shows what that looks like: a set of color keys such as color.ball, color.strike, color.out, color.on-base and color.walk, each with a value, and a favorites key which is empty by default.

To read a single setting, pass the key:

shell
playball config color.strike

To change it, pass the key and the value:

shell
playball config color.strike blue

And to go back to the default, pass the key with the --unset flag:

shell
playball config color.strike --unset

The storage behind it is the conf package, which keeps a configuration file in the usual place for the platform, so the settings survive between runs and the same key works on every platform.

The colour values are more capable than a list of names suggests. Each of them accepts black, red, green, yellow, blue, magenta, cyan, white and grey, and any of those may be prefixed by bright- or light-, for example bright-green. The value default specifies the default text colour for the terminal. Hex colours such as #FFA500 are also accepted, and the README notes that the exact colour used will depend on your terminal settings, and that if your terminal does not support true color, the closest supported colour may be used.

Environment Variables for One Off Overrides

Every setting can also be overridden for a single run with an environment variable, without persisting it to the config file. The rule is mechanical: take the key, uppercase it, and replace . and - with _, then prefix it with PLAYBALL_.

So live-delay becomes PLAYBALL_LIVE_DELAY and color.on-base becomes PLAYBALL_COLOR_ON_BASE:

shell
PLAYBALL_LIVE_DELAY=30 playball

The README explains when this matters, and the answer is the container. A --rm container has no way to persist playball config changes between runs, so the environment variable is the only way to change a setting in that setup:

shell
docker run -it --rm -e PLAYBALL_LIVE_DELAY=30 paaatrick0/playball

That is a neat resolution of a real problem. A configuration file is the right default for a program you run on your own machine, and an ephemeral container has nowhere to put a file, so the same settings are reachable through a second mechanism with one naming rule.

Beyond the colours, the settings include favorites, a list of team codes to highlight in the schedule and standings views, sort-by-favorites to put those games first in the schedule, title to set the terminal title to the score of the current game, and live-delay, the number of seconds between live updates.

Beyond the Regular Season

The World Baseball Classic support is the one feature that is a configuration change rather than a view, and it is documented in three ways.

The persistent form sets the sport configuration:

shell
playball config sport wbc

The one-off form uses an environment variable for a single viewing:

shell
PLAYBALL_SPORT=wbc playball

And the container form combines the two:

shell
docker run -it --rm -e PLAYBALL_SPORT=wbc paaatrick0/playball

To switch back to MLB, the same config subcommand with the mlb value does it:

shell
playball config sport mlb

The fact that this is a setting rather than a separate binary tells you something about the implementation. The schedule and standings data come from a source that is parameterised by league, and the change is a different dataset rather than a different program.

The favourites setting deserves a note too, because of a small practical detail. The teams are identified by their short codes, such as ATL, AZ, BAL, BOS, CHC, CIN, CLE, COL, CWS, DET, HOU, KC, LAA, LAD, MIA, MIL, MIN, NYM, NYY, OAK, PHI, PIT, SD, SEA, SF, STL, TB, TEX, TOR and WSH, and you can list several as a comma-separated value. The README warns that in some terminals the list must be quoted, for example playball config favorites "SEA,MIL".

The Stack: React on Blessed, and a Container Built in Stages

The implementation detail that explains most of the behaviour is that this is a React application rendering into a terminal. The dependencies include react at 17, react-blessed as the bridge between React and blessed, and redux with react-redux and the Redux Toolkit for state, which is heavier than a single screen of a terminal UI would obviously need and is the cost of using the React model for a live-updating scoreboard.

The build is Babel compiling src/ into dist/, with eslint over the .jsx and .js sources, and the tests run on the built-in node test runner with the node --test command. The published package contains only the bin and dist directories, and the entry point is bin/playball.js. There is a figlet dependency, which is the ASCII art library, so the scoreboard headings are drawn with it.

The Dockerfile is the most carefully written part of the repository. It is based on the documented Node.js containerising guide, uses a multi-stage build with separate base, deps, build-deps, build and production stages, pins the Node version to 24.11.1-alpine, creates a non-root user with a fixed group and user id of 1001, and uses npm cache mounts for the dependency installs. The production stage copies only what the running application needs.

So the container is not a convenience wrapper around a development environment, it is a deliberately small image with a non-root user, which is the right way to ship a program that is likely to run on a shared machine or a NAS.

Editorial conclusion

playball fits a person who wants to keep an eye on a game without opening a browser, especially someone on a server or in a terminal multiplexer, and it does not fit a viewer who wants the video, since it is a text rendering of the game state rather than a stream. Before installing, check the colour names against your terminal, because the settings accept named colours, bright and light prefixes and hex values, and the README notes that the closest supported colour may be used when true color is unavailable, and note that the Docker image defaults to Eastern Time and the timezone has to be set with TZ. The project is MIT licensed, the newest release is v3.6.0 from 2026-09-02, and the last push was on 2026-10-04.

Frequently asked questions

How do I run playball?

Try it with `npx playball`, install it globally with `npm install -g playball` and then run `playball`, or use the container with `docker run -it --rm paaatrick0/playball` if you do not have Node.js installed.

How do I configure playball?

The config subcommand lists the current values when run alone, reads one setting when passed a key, changes one when passed a key and a value, and reverts one to its default when passed the key with the --unset flag. Settings are stored in a key value file and persist between runs.

Can I change a setting for a single run?

Yes. Every setting can be overridden with an environment variable by uppercasing the key, replacing . and - with _, and prefixing it with PLAYBALL_, so live-delay becomes PLAYBALL_LIVE_DELAY. The README notes this is especially useful with Docker, where a --rm container cannot persist config changes.

How do I watch the World Baseball Classic with playball?

Set the sport configuration with `playball config sport wbc`, or use the environment variable for one run with `PLAYBALL_SPORT=wbc playball`. To switch back, run `playball config sport mlb`.

Which colours and teams can I configure?

Each display element has its own colour setting, and the values accept black, red, green, yellow, blue, magenta, cyan, white and grey, optionally prefixed with bright- or light-, plus the value default and hex colours such as #FFA500. The favorites setting takes team codes such as SEA or MIL, comma separated for several.

Official sources

  1. Issues
  2. License: MIT
  3. paaatrick/playball on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/paaatrick-playball.svg)](https://hysenlabs.com/projects/paaatrick-playball)

Was this useful?