DEV Community

YMori
YMori

Posted on • Edited on

I Tried Streamlit for the First Time and Built an MLB Bat Tracking Dashboard

What I Built

I built a web dashboard to visualize MLB bat tracking data from Baseball Savant.

👉 MLB Bat Tracking Dashboard

Dashboard top page

It has 5 tabs:

Tab What it shows
Leaderboard Rankings by Bat Speed, Attack Angle, and more
Player Comparison Radar & bar charts comparing up to 6 players
WBC Country Strength Batting & pitching scores for all 20 WBC 2026 nations
Team Lineup Builder Bat tracking metrics for each MLB team's 9-man lineup
Monthly Trend Month-by-month bat speed trend for any player

It also supports English/Japanese language switching.

Data Source: savant-extras

I used my own OSS library savant-extras to fetch the data.

pip install savant-extras
Enter fullscreen mode Exit fullscreen mode

It's a Python library for fetching bat tracking data from Baseball Savant. I built it because the existing pybaseball library didn't support date range filtering for this endpoint.

from savant_extras import bat_tracking, bat_tracking_monthly

# Batter bat tracking data for 2025 season
df = bat_tracking(year=2025, player_type="batter")

# Monthly breakdown
df_monthly = bat_tracking_monthly(year=2025)
Enter fullscreen mode Exit fullscreen mode

Why I Chose Streamlit

I wanted to share my data analysis in a visual, accessible format. Streamlit caught my eye for three reasons:

  • Pure Python — no HTML, CSS, or JavaScript needed
  • Free hosting on Streamlit Community Cloud (just connect your GitHub repo)
  • UI components in one linest.selectbox(), st.slider(), done

For example, this is all you need to add a dropdown to your sidebar:

import streamlit as st

year = st.sidebar.selectbox("Season", [2024, 2025], index=1)
player_type = st.sidebar.selectbox("Player type", ["batter", "pitcher"])
Enter fullscreen mode Exit fullscreen mode

Key Implementation Points

@st.cache_data to avoid repeated API calls

Fetching data every time a user interacts with the page would be too slow. @st.cache_data caches the result so repeated calls with the same arguments return instantly.

@st.cache_data(ttl=3600)
def load_bat_data(year: int, player_type: str):
    return bat_tracking(year=year, player_type=player_type)
Enter fullscreen mode Exit fullscreen mode

session_state to share data across tabs

Streamlit reruns the entire script on every user interaction. To keep data loaded after pressing a button, use st.session_state:

if load_btn:
    st.session_state["df_raw"] = load_bat_data(year, player_type)

if "df_raw" in st.session_state:
    df = st.session_state["df_raw"]
Enter fullscreen mode Exit fullscreen mode

matplotlib-fontja for Japanese font support

To render Japanese labels in matplotlib charts, I used matplotlib-fontja:

import matplotlib_fontja  # noqa: F401  ← just importing this enables Japanese fonts
Enter fullscreen mode Exit fullscreen mode

A Problem I Hit: japanize_matplotlib Broke on Python 3.13

After deploying to Streamlit Community Cloud, I got this error:

File "japanize_matplotlib/japanize_matplotlib.py", line 5, in <module>
    from distutils.version import LooseVersion
ModuleNotFoundError
Enter fullscreen mode Exit fullscreen mode

distutils was deprecated in Python 3.12 and fully removed in 3.13. I tried pinning the Python version with a runtime.txt file (python-3.11), but it didn't work — Streamlit Cloud still used 3.13.

The fix was simple: switch to matplotlib-fontja, which doesn't use distutils.

- japanize-matplotlib>=1.1
+ matplotlib-fontja
Enter fullscreen mode Exit fullscreen mode

Data Caveats

A few things to keep in mind about this dashboard:

  • Only MLB-rostered players are included. NPB and minor league players are not
  • Name matching has limitations — spelling variations or shared names may not match correctly
  • WBC 2026 scores are provisional — rosters are based on Baseball America's February 2025 projections and may differ from actual rosters
  • Bat tracking accuracy depends on Baseball Savant's data quality

Please treat the results as a reference, not a definitive source.

Summary

  • Used savant-extras (my own OSS library) for data fetching
  • Built a 5-tab dashboard with Streamlit
  • Deployed for free on Streamlit Community Cloud

Streamlit was easier than I expected. Tabs with st.tabs(), accordions with st.expander(), column layouts with st.columns() — learning just those three got me most of the way there.

If you want to turn your data analysis into something you can actually share, Streamlit is a great option.

Links

Top comments (5)

Collapse
 
maame-codes profile image
Maame Afua A. P. Fordjour

How long did it take you to get the hang of Streamlit compared to other tools you've used?

Collapse
 
yasumorishima profile image
YMori

Honestly, Streamlit was surprisingly quick to pick up — I had a basic interactive chart running within a day. The hardest part wasn't Streamlit itself but figuring out the data side (filtering, state management, etc.). If you're already comfortable with pandas, I think you can build something useful in a weekend. Definitely worth trying!

Collapse
 
maame-codes profile image
Maame Afua A. P. Fordjour

Thank you for the feedback will keep that in mind :)

Thread Thread
 
yasumorishima profile image
YMori

Good luck with your projects! 😊

Thread Thread
 
maame-codes profile image
Maame Afua A. P. Fordjour

Thank you YMori!