This is a fan-made, digital "side-along" companion app for the Heat: Pedal to the Metal board game. It is designed to enhance your tabletop experience by providing real-time race tracking, championship management, and interactive map visualization.
- ๐๏ธ Racer Tracking - Keep track of all players' positions and status on the board.
- ๐บ๏ธ Interactive Maps - Use official-style GeoJSON tracks or upload a photo of your own game board to use as a background.
- ๐ค AI Board Extraction - (Alpha) Snap a photo of your board and let the AI attempt to trace the track spaces for digital tracking.
- ๐ Championship Management - Automated points calculation and historical record-keeping across multiple race seasons.
- โก Digital Dashboard - A live, synchronized view for all players to see gaps, rankings, and fastest laps.
- ๐ Statistics & Analytics - Advanced stats including head-to-head comparisons, points progression charts, streaks, ELO ratings, and CSV export.
# Clone and setup
git clone <repository-url>
cd heat
# Install dependencies
go mod download
# Build
CGO_ENABLED=1 go build -o heat-server .
# Run
./heat-serverOpen http://localhost:6270 in your browser.
| Component | Version | Notes |
|---|---|---|
| Go | 1.21+ | Backend runtime |
| SQLite3 | - | Development libraries required |
| GCC | - | For CGO compilation |
| Docker | 20.10+ | Optional, for containerized deployment |
| Docker Compose | 1.29+ | Optional |
sudo apt-get update
sudo apt-get install -y build-essential libsqlite3-dev# With Homebrew
brew install go sqlite3
xcode-select --install.
โโโ main.go # Application entry point & route setup
โโโ main_test.go # Integration tests
โโโ paths_test.go # Path configuration tests
โโโ go.mod / go.sum # Go module definition
โโโ heat.db # SQLite database (created at runtime)
โโโ Dockerfile # Multi-stage Docker build
โโโ docker-compose.yml # Docker Compose orchestration
โโโ Taskfile.yml # Task runner
โโโ app/ # Application state (DB, sessions, globals)
โ โโโ app.go
โโโ db/ # Database initialization & migrations
โ โโโ db.go
โโโ handlers/ # HTTP handlers by domain
โ โโโ auth.go # Login, logout, setup
โ โโโ racers.go # Racer CRUD
โ โโโ race.go # Race info & history
โ โโโ tracks.go # Track CRUD & AI extraction
โ โโโ upload.go # File upload & management
โ โโโ quotes.go # Quote management
โ โโโ settings.go # AI, email, notification, Umami settings
โ โโโ email.go # Email sending & racer emails
โ โโโ stats.go # Statistics & analytics
โโโ middleware/ # HTTP middleware
โ โโโ middleware.go # Auth, rate limiting, security headers, Umami
โโโ models/ # Data structures
โ โโโ models.go
โโโ ws/ # WebSocket broadcasting
โ โโโ ws.go
โโโ static/ # Frontend assets
โ โโโ index.html # Main web interface
โ โโโ admin.html # Admin dashboard
โ โโโ controller.html # Race controller view
โ โโโ stats.html # Statistics view
โ โโโ chat.html # Commentary chat
โ โโโ style.css # Styling
โ โโโ js/ # Compiled TypeScript
โโโ ts/ # TypeScript source
โโโ tests/ # Playwright e2e tests
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/racers |
No | List all racers |
POST |
/api/racers |
Yes | Create/update a racer |
DELETE |
/api/racers |
Yes | Delete a racer |
GET |
/api/race-info |
No | Get current race info |
POST |
/api/race-info |
Yes | Update race info |
GET |
/api/race-history |
No | Get race history (season or oneoff) |
POST |
/api/race-history |
Yes | Save a race result to history |
DELETE |
/api/race-history |
Yes | Delete a race history entry |
GET |
/api/oneoff-races |
No | Get one-off (non-season) races |
DELETE |
/api/oneoff-races |
Yes | Delete a one-off race |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/tracks |
No | List all tracks |
POST |
/api/tracks |
Yes | Create/update a track |
DELETE |
/api/tracks |
Yes | Delete a track |
POST |
/api/tracks/ai-extract |
Yes | AI track extraction from image |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/racer-stats |
No | Get racer career statistics |
POST |
/api/racer-stats |
Yes | Update racer statistics manually |
GET |
/api/track-stats |
No | Get per-track statistics (wins, races, fastest laps) |
GET |
/api/stats/head-to-head |
No | Head-to-head comparison between two racers |
GET |
/api/stats/points-progression |
No | Cumulative points progression over a season |
GET |
/api/stats/streaks |
No | Win, podium, and DNF streaks for all racers |
GET |
/api/stats/elo |
No | ELO-style ratings based on race results |
GET |
/api/stats/export |
No | Export all racer stats as CSV |
GET |
/api/stats/track-performance |
No | Per-track performance breakdown by racer |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST |
/api/login |
No* | Login or create admin account |
POST |
/api/logout |
No | Logout and clear session |
GET |
/api/check-setup |
No | Check if admin is configured |
GET |
/api/ai-settings |
Yes | Get AI extraction settings |
POST |
/api/ai-settings |
Yes | Update AI extraction settings |
GET |
/api/notification-settings |
Yes | Get Gotify notification settings |
POST |
/api/notification-settings |
Yes | Update notification settings |
POST |
/api/test-notification |
Yes | Send a test notification |
GET |
/api/email-settings |
Yes | Get SMTP email settings |
POST |
/api/email-settings |
Yes | Update SMTP email settings |
GET |
/api/racer-emails |
Yes | Get racer email addresses |
POST |
/api/racer-emails |
Yes | Save a racer's email address |
POST |
/api/send-race-email |
Yes | Manually send race results email |
GET |
/api/umami-settings |
Yes | Get Umami analytics settings |
POST |
/api/umami-settings |
Yes | Update Umami settings |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/uploads |
No | List recent file uploads |
POST |
/api/upload |
Yes | Upload an image file |
GET |
/api/quotes |
No | List all quotes |
POST |
/api/quotes |
Yes | Add a new quote |
PUT |
/api/quotes |
Yes | Update a quote |
DELETE |
/api/quotes |
Yes | Delete a quote |
GET |
/api/quote/random |
No | Get a random quote |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/version |
No | Get application version |
GET |
/ws |
No | WebSocket endpoint for live race updates |
GET |
/docs |
No | Swagger UI documentation |
GET |
/api-docs |
No | OpenAPI spec (JSON) |
* Login endpoint requires setup flag for initial admin creation. After setup, standard credentials required.
Compare two racers across all season races they've both participated in:
GET /api/stats/head-to-head?racer1=1&racer2=2
Returns total races together, wins per racer, and average finishing positions.
Track cumulative points growth for any racer across a season:
GET /api/stats/points-progression?racer_id=1
Returns an ordered array of cumulative points after each race, suitable for line charts.
See which racers have the longest active and all-time streaks:
GET /api/stats/streaks
Tracks three streak types per racer:
- wins - Consecutive 1st place finishes
- podiums - Consecutive top-3 finishes
- dnf - Consecutive Did Not Finish results
ELO-style skill ratings computed from all pairwise race results:
GET /api/stats/elo
Uses K=32 with 1500 starting rating. Each race creates pairwise comparisons between every two racers. Ratings update after each race and are sorted descending.
Download all racer statistics as a CSV file:
GET /api/stats/export
Returns a CSV file with columns: ID, Name, Car, Points, Rank, Races, Wins, Gold, Silver, Bronze, Fastest Laps, DNF, DNS.
Analyze performance per track:
GET /api/stats/track-performance # Overview of all tracks
GET /api/stats/track-performance?racer_id=1 # Per-racer track breakdown
Without racer_id: returns track summaries with unique driver counts. With racer_id: returns wins, podiums, races, and average finishing position per track for that racer.
docker build -t heat-server .# Start the application
docker-compose up -d
# View logs
docker-compose logs -f heat-server
# Stop the application
docker-compose downThe application will be available at http://localhost:6270
- Local: Database stored as
heat.dbin the working directory; images instatic/images - Docker: Database stored in
heat-dbvolume at/db/heat.db; images inheat-datavolume at/app/images
To remove persistent data:
docker-compose down -v# Stop current container
docker-compose down
# Rebuild and start
docker-compose build && docker-compose up -d| Error | Solution |
|---|---|
no C compiler found |
Install build essentials: sudo apt-get install build-essential |
sqlite3.h not found |
Install dev packages: sudo apt-get install libsqlite3-dev |
| Issue | Solution |
|---|---|
| Port 6270 in use | Change port in docker-compose.yml |
| Database locked | Ensure only one instance is running |
MIT License - see LICENSE for details.