Small Google Apps Script web app to log friendly chess matches and compute session/player statistics.
- Provides a simple HTML form (
index.html) that calls server functions incode.gsto record matches. - Stores raw match rows in a Google Sheet (
Matches) and derives session summaries (Sessions) and per-player session stats (SessionPlayers).
code.gs— server logic:addRow(formData),computeSessionStats(sessionId),saveSessionSummary(sessionId), helpers for spreadsheet access.index.html— client UI that posts form data viagoogle.script.run.deploy.sh— helper to push + deploy viaclasp.appsscript.json— Apps Script configuration.
- Authenticate
claspif needed:
clasp login- Ensure
.clasp.jsonin this folder contains the correctscriptId(the project may already include it). - Deploy with:
./deploy.shNote: deploy.sh uses clasp and will push code.gs/index.html/appsscript.json and create/update a web deployment.
The chess tracker includes an owner-only admin panel for managing configuration without accessing Google Apps Script settings.
Regular Form: https://script.google.com/macros/s/YOUR_SCRIPT_ID/exec
Admin Panel: https://script.google.com/macros/s/YOUR_SCRIPT_ID/exec?admin=true
- Owner-only access: Only the script owner can access the admin panel
- Google OAuth: Authentication handled automatically via Google account
- No passwords required: Uses Session.getEffectiveUser() for verification
- Optional configuration: Set
ADMIN_OWNER_EMAILin Script Properties to explicitly specify the admin email
- Players Management:
- Add/remove/reorder players
- Customize player colors (color picker)
- View player emojis
- Venues Management:
- Add/remove venues
- Toggle mulligan venue settings
- Session Settings:
- Adjust session gap hours (1-99)
- Validation:
- Minimum 1 player and 1 venue required
- Session gap hours must be 1-99
- Empty player/venue names filtered out
- Sessions Tab 📊 - View and manage session data with pagination
- Data Tab 💾 - Export data (CSV/JSON), recompute statistics
- Analytics Tab 📈 - Overall statistics dashboard
- Audit Log Tab 📋 - Track admin actions
- Access admin panel with
?admin=trueparameter - Edit configuration:
- Modify players, venues, or session settings
- Changes are validated in real-time
- Save configuration:
- Click "Save Configuration" button
- Changes persist to Script Properties
- Immediately available in the main form
- Reset: Click "Reset" to reload current configuration from server
- Owner verification on every admin API call
- All admin actions logged with user email
- Input validation on server-side
- Non-owners see "Access Denied" page
- No sensitive data exposed in client
"Access Denied" error:
- Verify you're logged into the correct Google account
- Ensure you own the Apps Script project
- Optional: Set
ADMIN_OWNER_EMAILin Script Properties
Changes not appearing in form:
- Hard refresh the form page (Ctrl+Shift+R or Cmd+Shift+R)
- Clear browser cache if needed
- Verify changes saved successfully (look for success message)
The chess tracker displays real-time statistics for the current session directly in the form interface.
- Session Selector Dropdown: Switch between viewing current session, previous sessions, or Year-to-Date stats
- Session Description: Shows date, match count, venue, day of week, and time of day (e.g., "3/15 12 Matches at Home on a Saturday afternoon")
- Color Breakdown: Visual bar showing White wins vs Black wins vs Draws with percentages
- Player Stats Table: Per-player statistics including:
- Games played
- Wins/Losses/Draws
- Win percentage (color-coded: red <25%, orange <50%, yellow <75%, green 75%+)
- Brutality inflicted/suffered
- Auto-Refresh: Stats automatically update after each match submission
- Year-to-Date View: Aggregated stats across all sessions showing total matches, session count, and cumulative player performance
- On page load: Fetches current session data via
getCurrentSessionData() - Session selection: Dropdown allows viewing any past session or YTD stats
- After submission: Stats automatically refresh to show updated standings
- Fallback logic: If Sessions/SessionPlayers sheets don't exist, computes stats directly from Matches sheet
- Session stats: Reads from pre-computed
Sessionssheet for performance - Player stats: Reads from
SessionPlayerssheet with per-session breakdowns - YTD stats: Aggregates across all sessions via
getYearToDateStats()
The project includes a comprehensive automated test suite to ensure code quality and catch regressions.
- Open Apps Script Editor: Visit script.google.com and open your chess tracker project
- Select Test Function: In the function dropdown, choose either:
testAll()- Complete test battery (44 tests, ~10 seconds)quickTest()- Fast smoke test (core functionality only)
- Run Tests: Click the
▶️ Run button - View Results: Check Execution log (View → Logs or Ctrl+Enter)
The automated tests validate:
Configuration & Setup
- ✅ Configuration loading from Script Properties
- ✅ Default values (players, colors, venues, session gap)
- ✅ Player color mappings (Carlos=#7c3aed, Carey=#00d4ff, Jorge=#10b981)
- ✅ Validation constants and limits
Form Validation
- ✅ Required field enforcement
- ✅ Player name uniqueness (white ≠ black)
- ✅ Valid winner values (White|Black|Draw)
- ✅ Time limit required for "Time Out" ending
- ✅ Field length limits (players ≤50 chars, venue ≤100 chars)
Session Management
- ✅ Session ID generation (UUID format)
- ✅ Session assignment logic
- ✅ Session boundary detection (time gap and venue change)
Data Persistence
- ✅ Successful form submission to spreadsheet
- ✅ Match data written to Matches sheet
- ✅ Session stats computed and saved
- ✅ Rate limiting (1-second cooldown between submissions)
Error Handling
- ✅ Structured error logging with
handleError() - ✅ Error message preservation
- ✅ Graceful failure modes
Tests automatically clean up after themselves:
- Test matches are removed from Matches sheet (identified by "Test match" in notes)
- Test sessions removed from Sessions sheet
- Orphaned SessionPlayers entries cleaned up
If cleanup fails, manually run manualCleanupAllTestData() from the Apps Script editor.
- test-suite.gs - Server-side automated tests
- test-cleanup.gs - Test data cleanup utilities
- test-client.html - Client-side UI tests (deploy separately to test)
After code changes, verify:
Form Submission
- Submit valid match → success message appears
- Player dropdowns show emoji colors (🟣 Carlos, 🔵 Carey, 🟢 Jorge)
- Winner dropdown updates with correct players
- "Other" player fields appear/disappear correctly
- Time limit field required for "Time Out" ending
Session Display
- Current session stats display correctly
- Session analytics update after match submission
- Match history shows recent games
- Player badges show correct colors
Edge Cases
- Picture upload works (camera or file)
- Mulligan venue detection (creates venues list)
- Very long player/venue names are handled
- Rapid submissions blocked by rate limiting
Browser Testing
- Chrome/Edge (Chromium)
- Firefox
- Safari
- Mobile browsers (iOS Safari, Chrome Mobile)
- Run
testAll()after every code change - Catches regressions immediately - Check execution logs - Logs show exactly which tests failed and why
- Test in multiple browsers - GAS HTML rendering varies slightly
- Submit real matches - End-to-end testing catches issues automated tests miss
- Review spreadsheet data - Verify data format and calculations are correct
- Monitor error logs - Check Apps Script executions for production errors
For automated testing on every commit:
# Push code to Apps Script
clasp push
# Run tests via Clasp (requires API executable setup)
clasp run testAllNote: clasp run requires the script to be deployed as an API executable. See Clasp documentation for setup.
The app stores everything in a Google Spreadsheet whose ID is kept in Script Properties (SPREADSHEET_ID). If missing, the app will search Drive by name (Friend Chess Games) or create a new spreadsheet.
-
Matches(one row per logged match) columns:- Timestamp
- White Player
- Black Player
- Winner (White|Black|Draw)
- Game Ending
- Time Limit
- Venue
- Brutality (0-5)
- Notes
- Picture URL
- White Mulligan (Yes/No)
- Black Mulligan (Yes/No)
- Session ID
-
Sessions(one row per session) columns:- Session ID, Start Time, End Time, Matches, White Wins, Black Wins, Draws, Avg Brutality, Last Updated
-
SessionPlayers(one row per session+player) columns:- Session ID, Player,
- Matches, Wins, Wins as White, Wins as Black,
- Losses, Losses as White, Losses as Black,
- Draws, Draws as White, Draws as Black,
- Inflicted, Suffered, Last Updated
Session data are computed from Matches by computeSessionStats(sessionId) and persisted by saveSessionSummary(sessionId).
- Session IDs are assigned server-side when
addRow()runs if the client did not supply one. - A new session starts when:
- The time since the last match exceeds the configured gap (default: 6 hours, configurable via
SESSION_GAP_HOURS) - OR the venue explicitly changes from the previous match (even within the time window)
- The time since the last match exceeds the configured gap (default: 6 hours, configurable via
- If a match has a winner, the winner 'inflicted' the
Brutalityvalue; the loser 'suffered' it. - For a draw, both players are credited as having 'suffered' the brutality value.
- The client can send a base64 data URI (e.g.
data:image/png;base64,...). Server decodes and uploads to Drive and sets public sharingANYONE_WITH_LINK.
- Script Properties keys used:
SPREADSHEET_ID— target spreadsheet IDLAST_SUBMISSION— timestamp used to enforce a 1 second cooldown between submissions
- Logging: Use
logEvent(eventName, data)for structured logs. saveSessionSummaryis called afteraddRow()but wrapped so session-summary errors do not block match logging.- Header styling: new sheets add a header row with background
#4a90e2and white bold text.
If you want dedicated per-player sheets (Carey/Carlos/Jorge) for faster lookups, the repo can optionally sync SessionPlayers into separate player sheets — this is not enabled by default.
A Google Apps Script-optimized chess game tracker that logs your games and analyzes your performance over time. Uses proven Google Apps Script patterns for reliable deployment and data storage in Google Sheets.
- ♟️ Complete Game Logging: Track opponent, result, ratings, time control, and platform
- 📊 Live Session Stats: Real-time session statistics with player standings, win/loss breakdown, and match history
- 📈 Year-to-Date Stats: View aggregated statistics across all sessions for the current year
- 🏆 Rating Tracking: Monitor both your rating and opponent ratings (0-4000 range)
- ⏱️ Time Control Support: From bullet to classical and correspondence games
- 🎯 Result Analysis: Win/Loss/Draw tracking with visual feedback
- 📖 Opening Tracking: Record which openings you played
- 📝 Game Notes: Add insights and key moments from each game
- 📱 Mobile-Friendly: Responsive design works on all devices with 6 breakpoints
- 🔒 Reliable Design: Uses only proven Google Apps Script patterns
- 📊 Direct Google Sheets Integration: No external APIs - direct server-side sheet writing
- Google Account (for Apps Script and Sheets)
-
Open Google Apps Script:
- Go to script.google.com
- Click "New Project"
-
Replace the Server Code:
- Select all default code in
Code.gsand delete it - Copy entire contents of
code.gsfrom this repository - Paste into Google Apps Script editor
- Save (Ctrl/Cmd + S)
- Select all default code in
-
Add the HTML File:
- Click "+" next to "Files" → Select "HTML"
- Name it exactly "index" (no file extension)
- Delete any default content
- Copy entire contents of
index.htmlfrom this repository - Paste into the HTML editor
- Save (Ctrl/Cmd + S)
-
Deploy as Web App:
- Click "Deploy" → "New deployment"
- Click gear icon ⚙️ next to "Type" → Select "Web app"
- Execute as: "Me"
- Who has access: "Anyone" (or "Anyone with Google account")
- Click "Deploy"
- Authorize when prompted (grant all permissions)
- Copy the Web app URL
-
Test the Form:
- Visit the deployment URL
- Fill out and submit a test game
- Check your Google Drive for the automatically created "Chess Game Data" spreadsheet
chess-tracker/
├── code.gs # Server-side Google Apps Script code (DEPLOY THIS)
├── index.html # Complete HTML form (deploy as "index")
├── appsscript.json # Google Apps Script configuration
├── README.md # This file - includes deployment instructions
└── CLAUDE.md # Developer guidance
Making changes to the form is simple:
-
Clone the repository:
git clone https://github.com/texican/chess-tracker.git cd chess-tracker -
Edit files directly:
- Modify
code.gsfor server-side changes - Modify
index.htmlfor form and styling changes - No build process required
- Modify
-
Test changes:
- Copy updated files to Google Apps Script
- Deploy and test functionality
- All files are ready-to-deploy
For a more professional development experience, you can use Google Clasp:
# Install Clasp globally
npm install -g @google/clasp
# Login to your Google account
clasp login
# Create new Apps Script project
clasp create --type standalone --title "Chess Game Tracker"
# Push to Google Apps Script (files are already in correct structure)
clasp push
# Deploy as web app
clasp deploy- Opponent (required): Username or name of your opponent
- Result (required): Win, Loss, or Draw with visual indicators
- My Rating: Your rating before the game (0-4000)
- Opponent Rating: Opponent's rating (0-4000)
- Time Control (required): Bullet, Blitz, Rapid, Classical, or Correspondence
- Platform: Chess.com, Lichess, Chess24, ICC, FICS, Over the Board, or Other
- Opening: Opening played (e.g., Sicilian Defense, Italian Game)
- Notes: Game insights, key moments, what you learned
The form automatically creates/uses a Google Sheet with columns:
- Timestamp
- Opponent
- Result
- My Rating
- Opponent Rating
- Time Control
- Platform
- Opening
- Notes
This form uses only proven Google Apps Script patterns:
✅ Google Apps Script function calls - Direct server-side function invocation
✅ google.script.run API - Prevents blank page navigation issues
✅ Server-side Google Sheets API - Direct sheet writing without external APIs
✅ Simple vanilla JavaScript - No ES6 modules or modern APIs that fail in GAS
✅ Inline event handlers - Reliable event handling in GAS sandbox
✅ Properties Service - Persistent configuration storage
❌ Modern fetch() API - unreliable in GAS sandbox
❌ Complex event listeners - often fail in GAS environment
❌ ES6 modules - not supported in GAS HTML service
❌ External API calls - unnecessary and unreliable
❌ Modern JavaScript frameworks - too complex for GAS
- Robust error handling with detailed logging
- Input validation with length limits and required field checks
- Automatic spreadsheet creation if none exists
- Fallback spreadsheet logic if access issues occur
- Properties Service integration for configuration persistence
- Rate limiting with 1-second cooldown using Script Properties
- Rating validation with 0-4000 range clamping
This application uses Google Apps Script's Properties Service for persistent storage:
// Store last submission timestamp to prevent spam
PropertiesService.getScriptProperties().setProperty('LAST_SUBMISSION', now.toString());
const LAST_SUBMISSION = PropertiesService.getScriptProperties().getProperty('LAST_SUBMISSION');// Store and retrieve the target spreadsheet ID
PropertiesService.getScriptProperties().setProperty('SPREADSHEET_ID', spreadsheetId);
let spreadsheetId = PropertiesService.getScriptProperties().getProperty('SPREADSHEET_ID');Properties Stored:
SPREADSHEET_ID- ID of the Google Sheet used for data storageLAST_SUBMISSION- Timestamp of last form submission for rate limiting
This chess tracker is fully configurable via Script Properties. All player names, venues, and feature settings can be customized without modifying code.
Set these properties in Google Apps Script → Project Settings → Script Properties:
| Property Key | Type | Default | Example | Description |
|---|---|---|---|---|
SPREADSHEET_ID |
String | (auto-create) | 1abc...xyz |
Target Google Sheet ID |
PLAYERS |
Comma-separated | Player 1,Player 2,Player 3 |
Alice,Bob,Carol |
Chess player names for dropdown |
VENUES |
Comma-separated | Home,Park |
Home,Work,Cafe |
Game location options |
MULLIGAN_VENUES |
Comma-separated | (none) | Home,Backyard |
Venues where mulligans are allowed |
SESSION_GAP_HOURS |
Number | 6 |
4 |
Hours between matches to start new session |
- Open your Google Apps Script project
- Click Project Settings (⚙️ icon in left sidebar)
- Scroll to Script Properties section
- Click Add script property for each setting you want to customize
- Enter the property key and value
- Click Save script properties
- Redeploy your web app for changes to take effect
Script Properties Reference
All configuration is stored in Project Settings > Script Properties in the Apps Script editor.
| Property | Format | Example | Description |
|---|---|---|---|
PLAYERS |
Comma-separated names | Carlos,Carey,Jorge |
List of player names for dropdowns |
PLAYER_COLORS |
Name:HexColor pairs | Carlos:#7c3aed,Carey:#00d4ff,Jorge:#10b981 |
Hex color codes for each player |
PLAYER_EMOJIS |
Name:Emoji pairs | Carlos:🟣,Carey:🔵,Jorge:🟢 |
Emoji visual identifiers for each player |
VENUES |
Comma-separated locations | Home,Park,Coffee Shop |
List of venue options |
MULLIGAN_VENUES |
Comma-separated venues | Home,Chess Club |
Venues where mulligan tracking appears |
SESSION_GAP_HOURS |
Number | 6 |
Hours between matches to start new session |
Example 1: Different Players with Colors and Emojis
Property: PLAYERS
Value: Alice,Bob,Charlie,Diana
Property: PLAYER_COLORS
Value: Alice:#7c3aed,Bob:#00d4ff,Charlie:#10b981,Diana:#f59e0b
Property: PLAYER_EMOJIS
Value: Alice:🟣,Bob:🔵,Charlie:🟢,Diana:🟠
This will populate the player dropdowns with colored emoji prefixes (🟣 Alice, 🔵 Bob, 🟢 Charlie, 🟠 Diana).
Example 2: Custom Venues
Property: VENUES
Value: Home,Coffee Shop,Chess Club,Park
This will show these four venues in the venue dropdown.
Example 3: Enable Mulligan at Multiple Venues
Property: MULLIGAN_VENUES
Value: Home,Chess Club
The mulligan section will appear when either "Home" or "Chess Club" is selected as the venue.
Example 4: Shorter Session Gap
Property: SESSION_GAP_HOURS
Value: 4
Matches played within 4 hours will be grouped into the same session.
This configuration system makes the chess tracker fully clone-friendly:
- No need to edit code files to customize for your group
- Each deployment can have different players/venues
- Easy to share with friends - they just set their own Script Properties
- All sensitive/personal data stays in your Script Properties (not in code)
To configure the form to use your existing Google Sheet instead of creating a new one:
-
Get your Sheet ID from the URL:
https://docs.google.com/spreadsheets/d/1ABC123xyz.../edit ^^^^^^^^^^^ This is your Sheet ID -
Set the Script Property in Google Apps Script:
- Open your Apps Script project
- Go to Project Settings (gear icon)
- Scroll to Script Properties
- Click Add script property
- Property:
SPREADSHEET_ID - Value:
1ABC123xyz...(your actual Sheet ID) - Click Save script property
Alternatively, edit the DEFAULT_SPREADSHEET_ID in code.gs:
const DEFAULT_SPREADSHEET_ID = '1ABC123xyz...'; // Replace with your Sheet IDYour existing sheet should have these column headers (will be added automatically if missing):
- Timestamp | Opponent | Result | My Rating | Opponent Rating | Time Control | Platform | Opening | Notes
- Input length limits (100 chars for opponent, 500 for notes)
- Required field validation with user feedback
- Rating validation (0-4000 range)
- Form sanitization before submission
- Parameter validation in
addRow() - Data type checking and conversion
- Rating clamping to valid ranges
- Error logging for debugging
- Safe spreadsheet operations
-
Form goes to blank page:
- ✅ Fixed: Uses google.script.run API for seamless submission
-
Submit button doesn't work:
- ✅ Fixed: Uses Google Apps Script function calls + inline handlers
-
No data in spreadsheet:
- Check Google Apps Script execution logs
- Verify deployment permissions (Execute as: Me, Access: Anyone)
- Check your Google Drive for "Chess Game Data" spreadsheet
-
Permission errors:
- Make sure Google Apps Script has Google Sheets API access
- Redeploy and reauthorize if needed
This chess tracker succeeds where others fail because it:
- Follows Google Apps Script limitations instead of fighting them
- Uses only proven patterns that work reliably in the GAS environment
- Eliminates modern JavaScript that breaks in GAS sandbox
- Provides reliable form submission with google.script.run API
- Handles all edge cases with proper error handling and fallbacks
With your game data in Google Sheets, you can easily:
- Track rating progress over time
- Analyze win rates by time control
- Study opening performance - which openings work best for you
- Platform comparison - performance across different chess sites
- Opponent analysis - track results against specific players
- Time control preferences - see where you perform best
- Fork the repository
- Make changes directly to
code.gsand/orindex.html - Test deployment in Google Apps Script
- Submit pull request
- All changes must work in Google Apps Script environment
- Test deployments before submitting changes
- Maintain compatibility with proven GAS patterns
- Update documentation when changing functionality
This project is licensed under the MIT License - see the LICENSE file for details.
- Issues: GitHub Issues
- Deployment Help: See deployment section above for detailed instructions
- Google Apps Script Docs: developers.google.com/apps-script
- Frontend: Vanilla JavaScript, HTML5, CSS3 (GAS-compatible)
- Backend: Google Apps Script (V8 runtime)
- Storage: Google Sheets API
- Architecture: Simple, reliable GAS patterns
- Deployment: Direct file copying (no build process)
Track your chess journey with reliable Google Apps Script deployment! ♟️