Competition track log analysis for hanggliding and paragliding. Load IGC files and tasks, and get instant in-browser analysis.
- Flight event detection — automatic detection of takeoff, landing, thermals, glides, start/goal crossings, and turnpoint tagging
- Thermal analysis — entry/exit times, altitude gain, average climb rate
- Glide analysis — distance, altitude lost, L/D ratio, plus sink detection for poor glides
- Task scoring — CIVL GAP scoring with distance, time, leading, and arrival points
- Task editor — create and edit tasks with drag-to-reorder turnpoints, waypoint database search, and click-on-map placement
- Multiple data sources — drag & drop IGC/XCTSK files, import from XContest by task code, or import from AirScore by URL
- Interactive map — 2D (Mapbox/Leaflet) and 3D views with track overlay, task cylinders, and map annotations
- Altitude sparkline — clickable time-series chart linked to events and map position
- Configurable units & detection — speed, altitude, distance, climb rate units; adjustable thermal/glide detection thresholds
- Bun (also requires Node.js 20+)
- A MapBox access token (for the map). If your token has URL restrictions, ensure
glidecomp.comandlocalhost:3000are in the allowed URLs list.
bun install
# Copy .env.example and add your MapBox token
cp .env.example .env
# Edit .env and set VITE_MAPBOX_TOKEN=your_token_hereStart the frontend and auth worker together (http://localhost:3000 + http://localhost:8788):
bun run devThis starts both the Vite dev server and the auth API worker. The auth worker requires a .dev.vars file in web/workers/auth-api/ — see docs/auth.md for setup.
To start only the frontend (without auth):
bun run dev:frontendTo use AirScore features, also start the AirScore API worker (http://localhost:8787) in a separate terminal:
bun run --filter airscore-api devThe frontend automatically detects the local worker. If you'd rather skip the worker and use the production API instead:
VITE_AIRSCORE_URL=https://glidecomp.com/api/airscore bun run devbun run test # Run tests
bun run typecheck # Type check root project
bun run typecheck:all # Type check everything (frontend + engine + workers)Read https://developer.chrome.com/blog/chrome-devtools-mcp-debug-your-browser-session
- Push to
master→ deploys to production - Push to other branches → deploys to preview URL
bun run deploy # Manual deploy to Cloudflare Pages
bun run deploy:worker # Manual deploy AirScore API Worker
bun run deploy:all # Deploy Pages + all WorkersURLs:
- Production: https://glidecomp.com
- Previews: https://{branch}.glidecomp.pages.dev
detect-events - Detect flight events from an IGC file, outputting CSV:
bun run detect-events -- <flight.igc> [task.xctsk]
# Example with sample data
bun run detect-events -- \
web/frontend/public/data/tracks/2025-01-05-Tushar-Corryong.igc \
web/frontend/public/data/tasks/buje.xctskget-xcontest-task - Download a task from XContest by code:
bun run get-xcontest-task -- face
bun run get-xcontest-task -- --file task.jsonscore-task - Score multiple pilots against a task using CIVL GAP (FAI Sporting Code Section 7F):
bun run score-task <task.xctsk> <igc-file-or-folder>... [options]
# Options:
# --scoring <PG|HG> Sport type (default: PG)
# --nominal-distance <m> Nominal distance in meters (default: 70% of task distance)
# --nominal-goal <ratio> Nominal goal ratio 0-1 (default: 0.2)
# --nominal-time <s> Nominal time in seconds (default: 5400)
# --min-distance <m> Minimum distance in meters (default: 5000)
# --no-leading Disable leading (departure) points
# --no-arrival Disable arrival points
# --json Output as JSON
# Example: score Corryong Cup 2026 Task 1 (HG, no leading/arrival)
bun run score-task \
web/engine/tests/fixtures/corryong-cup-2026-t1/task.xctsk \
web/engine/tests/fixtures/corryong-cup-2026-t1/ \
--scoring HG --no-leading --no-arrival \
--nominal-distance 35000 --nominal-goal 0.3Example output:
=== Task Scoring Results (CIVL GAP) ===
Task distance: 73.9 km
Pilots: 32 flying / 32 present
In goal: 12 (37.5%)
Best time: 1:37:43
Task Validity: 100.0%
Available Points: 1000 (dist: 486, time: 514, lead: 0, arr: 0)
# Pilot Dist Time Dist Pts Time Pts Lead Pts Arr Pts Total
------------------------------------------------------------------------------------------------------------
1 Jon Durand 73.9 km 1:37:43 485.6 514.4 0.0 0.0 1000
2 Rohan Holtkamp 73.9 km 1:46:05 485.6 386.9 0.0 0.0 872
3 Peter Burkitt 73.9 km 1:49:00 485.6 358.8 0.0 0.0 844
...
13 Rich Reinauer 72.0 km - 473.1 0.0 0.0 0.0 473
...
29 Rennick Kerr 5.0 km - 32.9 0.0 0.0 0.0 33
web/
frontend/ - Cloudflare Pages frontend (Vite + TypeScript)
engine/ - Shared analysis library (IGC parsing, event detection, GAP scoring)
cli/ - CLI utilities (detect-events, get-xcontest-task, score-task)
workers/
auth-api/ - Authentication API (Cloudflare Worker + D1)
airscore-api/ - AirScore caching proxy (Cloudflare Worker)
scripts/ - Operational scripts (secrets, test emails)
docs/ - Feature and architecture specifications