Tennis Analyzer is a micro-level tennis performance analysis mobile app. Use this app to track point-by-point match data and generate detailed statistical analysis, including serve percentages, high pressure points win percentage, serve direction patterns, and more. Coaches and players can use this app to conduct real-time, micro-level performance analysis, compare players, discover patterns, and identify areas for improvement.
This app may be used simply as a score tracker. However, to generate detailed statistical analysis, the user must enter information such as serve direction, shot count, last shot type, and shot result on a point by point basis. Therefore, this app requires a high level of concentration during the match.
The frontend is a native mobile app built using React Native and Expo, and the backend is built using Google Sheets and Google Apps Script.
backend/: Server-side logic.- Core File:
src/MicroStats.js - Handles data processing and statistical calculations.
- Core File:
frontend-native/: React Native mobile application.- Built with Expo, SQLite local cache, and native UI elements.
π Project Website: gamepointanalytics.github.io/tennis-analyzer/
- Google Play Store: Download for Android
- FAQ.pdf - Frequently Asked Questions
- Carlos Alcaraz vs Jannik Sinner (2022 US Open QF)
- Stefanos Tsitsipas vs Novak Djokovic (2023 Australian Open Final)
- Rafael Nadal vs Roger Federer (2017 Australian Open Final)
- Iga Swiatek vs Elena Rybakina (2023 Australian Open 4R)
- Ash Barty vs Danielle Collins (2022 Australian Open Final)
- Ash Barty vs Elena Rybakina (2022 WTA Adelaide Final)
- Brandon Holt vs Mikael Torpegaard (2018 NCAA, USC vs Ohio State)
- Mackenzie McDonald vs Yannick Hanfmann (2015 NCAA, UCLA vs USC)
Detailed AI-generated insights and analysis for individual players:
- Carlos Alcaraz - AI Insights
- Ash Barty - AI Insights
- Danielle Collins - AI Insights
- Roger Federer - AI Insights
- Rafael Nadal - AI Insights
- Elena Rybakina - AI Insights
- Jannik Sinner - AI Insights
- Iga Swiatek - AI Insights
- 490+ downloads, 80+ installed audience, and 30-40 monthly active users
The native mobile app lives in frontend-native/. It is built with Expo SDK 56 and
React Native, and packaged into a standalone APK using the Android Gradle build system.
| Tool | Notes |
|---|---|
| Node.js | v18 or later |
| Java JDK | v17 recommended (check with java -version) |
| Android SDK | Installed via Android Studio |
| ADB | Bundled with Android SDK; ensure it is on your PATH |
| USB debugging | Enabled on your Android phone |
# 1. Navigate to the native app folder
cd frontend-native
# 2. Install Node dependencies (only needed after cloning or npm install changes)
npm install
# 3. Generate the release APK
.\gradlew assembleReleaseThe APK is written to:
frontend-native\app\build\outputs\apk\release\app-release.apk
Build time is typically 2β5 minutes. Subsequent builds are faster due to Gradle's incremental cache.
Warning
If you run npx expo prebuild --clean (e.g. to update icons or change the package name),
it wipes the entire android/ folder including local.properties. You must recreate it:
# android/local.properties
sdk.dir=C\:\\Users\\yingz\\AppData\\Local\\Android\\Sdk
Without this file, the Gradle build will fail with "SDK location not found".
# Connect your phone via USB, then from the frontend-native folder:
adb install -r app\build\outputs\apk\release\app-release.apk-r= replace (reinstall over existing version, preserving local data)- Omit
-rfor a completely fresh install (wipes local SQLite data) - Run
adb devicesfirst to confirm your phone is recognized
The display name shown on the home screen is set in:
frontend-native\app.json β "name" and "displayName" fields
After changing, rebuild with .\gradlew assembleRelease.
# Check a single file
node --check src\screens\AnalysisScreen.js
# Check all JS files under src\
Get-ChildItem -Recurse -Filter "*.js" src | ForEach-Object {
$r = node --check $_.FullName 2>&1
if ($LASTEXITCODE -ne 0) { Write-Host "FAIL: $($_.Name) β $r" }
}
Write-Host "Done"The backend lives in backend/src/MicroStats.js. It runs entirely inside Google Sheets
as a Google Apps Script web app. To deploy updates:
- Open the Google Sheet β Extensions β Apps Script
- Copy the contents of
backend/src/MicroStats.jsinto the editor (replace existing) - Click Deploy β Manage Deployments β Edit (pencil icon)
- Bump the version, click Deploy
- Copy the new Web App URL if it changed and update the app's settings screen
To verify the deployed version matches your local file, search for a unique string
(e.g. doPost) in the Apps Script editor β if not found, the old version is deployed.
Caused by a missing android/local.properties file (wiped by npx expo prebuild --clean).
Fix: Recreate the file:
Set-Content frontend-native\android\local.properties "sdk.dir=C\:\\Users\\yingz\\AppData\\Local\\Android\\Sdk"Your system default Java is too old. Android Studio bundles JDK 21.
Fix: Set JAVA_HOME permanently (run PowerShell as Administrator, one-time):
[System.Environment]::SetEnvironmentVariable("JAVA_HOME", "C:\Program Files\Android\Android Studio\jbr", "Machine")Then close and reopen your terminal.
Windows has a 260-character path limit. CMake and Ninja enforce this regardless of the
LongPathsEnabled registry setting. If your project path is deep, C++ compilation fails.
Fix: Keep the project in a short root path like C:\dev\tennis-analyzer\.
If you move (or clone) the project to a different directory, the build will fail because Gradle, CMake, and node_modules all cache absolute paths from the old location.
Fix β full clean rebuild from the new location:
cd C:\dev\tennis-analyzer\frontend-native
# 1. Reinstall all npm packages (resolves paths to the new location)
Remove-Item -Recurse -Force node_modules
npm install
# 2. Regenerate the entire android/ folder
npx expo prebuild --platform android --clean
# 3. Recreate local.properties (always wiped by prebuild --clean)
Set-Content android\local.properties "sdk.dir=C\:\\Users\\yingz\\AppData\\Local\\Android\\Sdk"
# 4. Build
cd android
.\gradlew assembleReleaseImportant
You must do ALL four steps. Skipping any one of them will leave stale paths from the
old location cached in node_modules, android/app/.cxx, or .gradle build artifacts.
This command wipes and regenerates the entire android/ folder. You must:
- Recreate
android/local.properties(see above) - Run
.\gradlew clean assembleRelease(the C++ cache in.cxxis also wiped, so the first rebuild is slower β about 5β10 minutes)