A Steam/Strava-style activity tracker for Lichess players. This dashboard helps users transition from "mindless playing" to "intentional practice" by visualizing time spent at the board.
🤖 Note: This entire application was vibe-coded using AI assistance, showcasing rapid prototyping and iterative development.
GitHub Pages: https://ra314.github.io/lichess-time-tracker/
The app fetches real-time game data via the Lichess API, calculates total playtime (lastMoveAt - createdAt), and provides accountability metrics such as binge detection and peak performance windows.
- Frontend: HTML5, CSS3 (Custom Grid), Vanilla JavaScript (ES6+).
- Data Visualization: Chart.js for bar charts; Native CSS Grid for the calendar heatmap.
- Data Source: Lichess.org Open API (NDJSON Stream).
- Scrollable Calendar Heatmap: View your entire playing history in a calendar format with day labels, date numbers, and month headers
- Flexible Daily Goals: Set goals based on either time played or number of games, with customizable targets
- Game Type Filters: Filter heatmap data by Bullet, Blitz, Rapid, or Classical time controls
- Activity Levels: Color-coded intensity showing playing patterns
- Time Period Tracking: See the full span of your downloaded game history
- Export/Import: Save and restore your game data locally with integrity verification
- Real-time Progress: Watch as games download with live progress indicators
Playtime is calculated per game by finding the difference between the first and last move timestamps.
The "Binge Warning" triggers when a user plays 5 or more games where the total elapsed time between the start of the first game and end of the last game is less than 2 hours. This helps identify sessions where a user might be "tilting."
We calculate the win rate
index.html: Dashboard structure and CDN links.style.css: Dark-mode UI and custom heatmap styling.api.js: Logic for handling Lichess NDJSON streaming.app.js: Data processing engine and chart rendering logic.
- Daily Goals: Allow users to set a "mins/day" goal and color the heatmap green when achieved.
- Download Number: Number of games to download should be a configurable field on the UI.
- Progress Bar: Display real-time download progress showing the current date being processed and total number of games downloaded so far.
- Persistent Storing of Downloaded Data: Reduce load on lichess by storing loaded games.
- Add an import and export button.
- Export:
- Export is in JSON format.
- Export includes all of the games currently downloaded.
- Export contains metadata fields:
- SHA-256 hash of the export, to prevent unintentional tampering by users.
- Username of the user for whom the export was generated.
- Timestamp of the earliest and most recent game in the list of games.
- Export date and total game count.
- Import:
- Imports games and metadata from JSON file.
- Username field is populated with the username from the import.
- Data validation: Hash verification ensures no tampering has occurred.
- When sync button is pressed after import:
- Downloads all games from the timestamp of the most recent game to current timestamp.
- Downloads games previous to the earliest timestamp.
- Deduplicates games to prevent conflicts.
- OAuth2 Authentication Authenticated users can download at 60 games per second, which we would prefer.
- Click the Export button after syncing data
- A JSON file will be downloaded containing:
- All downloaded games
- Metadata (username, timestamps, hash)
- File naming:
lichess-games-{username}-{date}.json
- Click the Import button
- Select a previously exported JSON file
- The app will:
- Verify data integrity using SHA-256 hash
- Load all games into the dashboard
- Populate the username field
- After import, clicking Sync Data will:
- Fetch newer games (after most recent cached game)
- Fetch older games (before earliest cached game)
- Merge and deduplicate all games
- SHA-256 Hashing: All exports include a cryptographic hash to detect tampering
- Validation: Imports are rejected if the hash doesn't match the data
- Deduplication: Games are deduplicated by ID to prevent data corruption