Case Study 1: Redesigning a Football Analytics Dashboard
Overview
Client: Midwest State University Athletic Department Challenge: The existing analytics dashboard was being ignored by coaching staff Timeline: 3-week design sprint Outcome: 85% increase in daily dashboard usage by coaches
Background
The Problem
Midwest State's football program had invested significantly in analytics infrastructure. They had quality data pipelines, sophisticated EPA models, and comprehensive play-by-play tracking. However, the analytics team faced a frustrating reality: coaches weren't using the dashboard.
Usage metrics told the story: - Average daily logins: 2 (out of 15 coaching staff) - Average time on dashboard: 47 seconds - Feature utilization: Only the "overview" tab was accessed - Feedback: "Too complicated" and "Can't find what I need"
Initial Assessment
The analytics director invited us to evaluate the existing dashboard. Our assessment identified several fundamental problems:
Visual Design Issues: 1. Information density overwhelming—42 distinct metrics on the main screen 2. No visual hierarchy—all elements competed for attention equally 3. Color scheme using 12 different colors without clear meaning 4. Small text (10pt font) requiring users to lean into monitors 5. No responsive design—unusable on tablets during practice
Functional Issues: 1. 5-click minimum to access situational data 2. Filters reset on every page navigation 3. No saved views or personalization 4. Export functionality buried in submenu 5. 8-second load time for main dashboard
Conceptual Issues: 1. Organized by data source, not coaching question 2. No executive summary or key takeaways 3. Metrics displayed without context or benchmarks 4. No connection between insights and game film
The Redesign Process
Phase 1: User Research
We began by shadowing coaches to understand their actual workflow and needs.
Key Discoveries:
Offensive Coordinator (OC): - Checks analytics twice daily: morning prep and post-practice - Primary questions: "What worked yesterday?" and "What should we emphasize today?" - Preferred tablet access during film sessions - Wanted one-click access to play video from any data point
Defensive Coordinator (DC): - Heavy user during opponent game prep - Primary questions: "What does the opponent do in [situation]?" and "What are their tendencies?" - Needed printable scouting reports - Valued comparative analysis against multiple opponents
Head Coach: - Limited time—wanted 60-second overview maximum - Primary questions: "Are we improving?" and "Where are we vulnerable?" - Needed talking points for staff meetings - Required export for athletic director reports
Quality Control Staff: - Most sophisticated data users - Needed granular filtering and custom queries - Wanted bulk export capabilities - Required accuracy verification tools
Phase 2: Information Architecture
Based on user research, we reorganized the dashboard around coaching questions rather than data sources.
Old Structure (by data source):
├── Play-by-Play Data
├── Personnel Tracking
├── Efficiency Metrics
├── Opponent Scouting
├── Historical Trends
└── Player Statistics
New Structure (by coaching question):
├── Today's Priorities (Executive Summary)
├── Our Performance
│ ├── Offensive Analysis
│ ├── Defensive Analysis
│ └── Special Teams
├── Opponent Analysis
│ ├── Tendencies
│ ├── Key Matchups
│ └── Scouting Report
├── Player Development
│ ├── Individual Reports
│ └── Position Groups
└── Quick Tools
├── Film Search
├── Export Center
└── Custom Query
Phase 3: Visual Design System
We established a consistent visual language addressing the identified problems.
Color Palette:
# New semantic color scheme
colors = {
# Performance colors (diverging)
'elite': '#1a9641', # Dark green - top 20%
'good': '#a6d96a', # Light green - above average
'average': '#ffffbf', # Yellow - average
'below_avg': '#fdae61', # Orange - below average
'poor': '#d7191c', # Red - bottom 20%
# Categorical (team/opponent)
'us': '#002855', # School primary color
'opponent': '#6c757d', # Neutral gray
'league_avg': '#adb5bd', # Light gray for reference
# UI colors
'background': '#f8f9fa',
'card': '#ffffff',
'text': '#212529',
'text_secondary': '#6c757d'
}
Typography Hierarchy:
typography = {
'h1_dashboard': {'size': 24, 'weight': 700}, # Dashboard title
'h2_section': {'size': 18, 'weight': 600}, # Section headers
'h3_card': {'size': 14, 'weight': 600}, # Card titles
'body': {'size': 13, 'weight': 400}, # Body text
'data_large': {'size': 28, 'weight': 700}, # KPI values
'data_small': {'size': 11, 'weight': 400}, # Table data
'caption': {'size': 10, 'weight': 400}, # Footnotes
}
Component Library: We created reusable components ensuring consistency:
- KPI Card: Large number with trend indicator and sparkline
- Comparison Bar: Horizontal bar with benchmark line
- Situation Heatmap: Down × Distance matrix with EPA values
- Trend Line: Weekly performance with confidence bands
- Rank Indicator: Position badge with percentile context
The New Dashboard Design
Executive Summary (Head Coach View)
The new landing page answered "How are we doing?" in under 60 seconds.
Layout:
┌─────────────────────────────────────────────────────────┐
│ MIDWEST STATE FOOTBALL - Week 8 Dashboard │
├─────────────────────────────────────────────────────────┤
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ OFF EPA │ │ DEF EPA │ │ WIN PROB │ │ INJURY │ │
│ │ +0.18 │ │ +0.12 │ │ 68% │ │ STATUS │ │
│ │ ▲ +0.03 │ │ ▼ -0.02 │ │ vs. RIVAL│ │ 3 OUT │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
├─────────────────────────────────────────────────────────┤
│ THIS WEEK'S FOCUS AREAS │
│ ┌─────────────────────────────────────────────────┐ │
│ │ ⚠️ 3rd Down Conversion dropped to 38% (-7%) │ │
│ │ ✅ Red Zone TD% improved to 72% (+8%) │ │
│ │ ⚠️ Pressure Rate allowed increased to 32% │ │
│ └─────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────┤
│ TREND (Last 4 Weeks) OPPONENT PREVIEW │
│ [Sparkline charts] [Key matchup cards] │
└─────────────────────────────────────────────────────────┘
Design Decisions:
- Large KPI Cards: 28pt font for primary values, readable at 10 feet
- Trend Indicators: Arrow + percentage change, color-coded
- Focus Areas: Automated insight generation highlighting significant changes
- Limited Scope: Only 4 primary KPIs (reduced from 42)
- Progressive Disclosure: Click any card for detailed view
Offensive Analysis (OC View)
The offensive coordinator's view prioritized actionable play-calling insights.
Key Visualization: Situation Success Matrix
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
def create_situation_matrix(data, title="Offensive EPA by Situation"):
"""Create situational heatmap for offensive coordinator."""
fig, ax = plt.subplots(figsize=(10, 6))
# Down × Distance matrix
downs = ['1st', '2nd', '3rd']
distances = ['1-3', '4-6', '7-10', '11+']
# Sample EPA data
matrix = np.array([
[0.25, 0.18, 0.12, 0.05], # 1st down
[0.32, 0.15, 0.02, -0.12], # 2nd down
[0.45, 0.22, -0.08, -0.28] # 3rd down
])
# Create heatmap with diverging colors
cmap = sns.diverging_palette(10, 130, as_cmap=True)
im = ax.imshow(matrix, cmap=cmap, aspect='auto', vmin=-0.3, vmax=0.5)
# Add text annotations
for i in range(len(downs)):
for j in range(len(distances)):
value = matrix[i, j]
color = 'white' if abs(value) > 0.15 else 'black'
ax.text(j, i, f'{value:+.2f}', ha='center', va='center',
fontsize=14, fontweight='bold', color=color)
# Labels
ax.set_xticks(range(len(distances)))
ax.set_xticklabels(distances, fontsize=12)
ax.set_yticks(range(len(downs)))
ax.set_yticklabels(downs, fontsize=12)
ax.set_xlabel('Distance to First Down', fontsize=12, fontweight='bold')
ax.set_ylabel('Down', fontsize=12, fontweight='bold')
ax.set_title(title, fontsize=14, fontweight='bold', pad=10)
# Colorbar
cbar = plt.colorbar(im, ax=ax, shrink=0.8)
cbar.set_label('EPA/Play', fontsize=11)
plt.tight_layout()
return fig
Interactive Features: - Click any cell to see play-by-play breakdown - Filter by formation, personnel, or time period - One-click export to film review system
Opponent Scouting (DC View)
The defensive coordinator's view focused on opponent tendencies and game planning.
Key Visualization: Tendency Wheel
A circular visualization showing opponent play-calling tendencies by field zone:
def create_tendency_wheel(tendencies):
"""Create tendency visualization for opponent analysis."""
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
zones = ['Own 1-25', 'Mid-Field', 'Red Zone']
for ax, zone in zip(axes, zones):
zone_data = tendencies[zone]
# Pie chart for play type distribution
labels = ['Inside Run', 'Outside Run', 'Short Pass', 'Deep Pass']
sizes = [zone_data['inside_run'], zone_data['outside_run'],
zone_data['short_pass'], zone_data['deep_pass']]
colors = ['#2d4a6f', '#4a7ca8', '#7eb5d6', '#b8d4e8']
wedges, texts, autotexts = ax.pie(
sizes, labels=labels, autopct='%1.0f%%',
colors=colors, startangle=90,
textprops={'fontsize': 10}
)
ax.set_title(zone, fontsize=12, fontweight='bold')
plt.suptitle('Opponent Tendencies by Field Position',
fontsize=14, fontweight='bold')
plt.tight_layout()
return fig
Implementation Results
Quantitative Improvements
After launching the redesigned dashboard:
| Metric | Before | After | Change |
|---|---|---|---|
| Daily Active Users | 2 | 11 | +450% |
| Avg. Session Duration | 47 sec | 4.2 min | +436% |
| Pages per Session | 1.2 | 6.8 | +467% |
| Mobile Usage | 5% | 42% | +740% |
| Feature Utilization | 12% | 67% | +458% |
| Coach Satisfaction | 2.1/5 | 4.4/5 | +110% |
Qualitative Feedback
Offensive Coordinator:
"I actually look forward to checking the dashboard now. Before meetings, I pull up the situation matrix and immediately know what to emphasize. The film integration is a game-changer—I can go from data to video in one click."
Head Coach:
"I get what I need in 30 seconds. The focus areas tell me exactly where we improved and where we're struggling. I can speak intelligently about our analytics without spending an hour studying charts."
Quality Control:
"The quick tools section saved my job. Custom queries that used to take 20 minutes now take 2. And I can actually find things."
Key Design Lessons
Lesson 1: Design for the Question, Not the Data
The original dashboard was organized by data source because that's how the analytics team thought about their work. But coaches don't care about data pipelines—they care about answers.
Before: "Here's all our EPA data, play-by-play data, and efficiency metrics" After: "Here's whether we're improving and what to focus on today"
Lesson 2: Progressive Disclosure Prevents Overwhelm
Rather than showing everything at once, we layered information: - Level 1: Executive KPIs (4 numbers) - Level 2: Key insights and trends (click to expand) - Level 3: Detailed breakdowns (click individual cards) - Level 4: Raw data and exports (dedicated tools section)
Each user could engage at their preferred depth.
Lesson 3: Context Transforms Data into Insight
Raw numbers meant nothing without context. We added: - Benchmarks: League average, top 25%, historical performance - Trends: Directional change with magnitude - Comparisons: Us vs. opponent, us vs. last year - Thresholds: Color-coding for performance levels
Lesson 4: Mobile-First Isn't Just Responsive
True mobile design meant rethinking interactions: - Larger touch targets (minimum 44px) - Swipe gestures for navigation - Simplified visualizations - Offline capability for practice field use
Lesson 5: Speed Matters More Than Features
We prioritized performance over functionality: - Load time reduced from 8 seconds to 1.2 seconds - Pre-computed common queries - Lazy loading for secondary content - Local caching for frequently accessed data
Technical Implementation Notes
Technology Stack
- Frontend: React with D3.js for custom visualizations
- Backend: Python FastAPI for analytics processing
- Database: PostgreSQL with materialized views for common queries
- Caching: Redis for user session data and common computations
- Video Integration: Custom API bridge to video platform
Performance Optimizations
# Materialized view for common dashboard query
CREATE MATERIALIZED VIEW dashboard_summary AS
SELECT
team_id,
week,
AVG(CASE WHEN play_type = 'pass' THEN epa ELSE NULL END) as pass_epa,
AVG(CASE WHEN play_type = 'rush' THEN epa ELSE NULL END) as rush_epa,
SUM(CASE WHEN successful = true THEN 1 ELSE 0 END)::float / COUNT(*) as success_rate,
COUNT(*) as total_plays
FROM plays
WHERE season = current_season()
GROUP BY team_id, week
WITH DATA;
-- Refresh nightly
CREATE INDEX idx_dashboard_team_week ON dashboard_summary(team_id, week);
Discussion Questions
-
What other user roles might need customized dashboard views? How would their needs differ?
-
The redesign reduced metrics from 42 to 4 on the main screen. How do you decide what to cut when simplifying?
-
How would you handle situations where different coaches want contradictory features?
-
What metrics would you track to measure ongoing dashboard effectiveness?
-
How might this dashboard need to change during different parts of the season (preseason, rivalry week, bowl prep)?
Code Resources
Complete implementation code is available in code/case-study-code.py, including:
- Dashboard component library
- Color and typography utilities
- Sample visualizations
- Data transformation functions