Motivation

As a cyclist living in Colorado, I have access to incredible terrain. The problem is there's almost too much of it. With thousands of possible routes, picking the right one for a given day becomes its own challenge. Do I want something flat and fast, or am I chasing elevation? Do I have 45 minutes or 4 hours? I found myself defaulting to the same tired loops instead of exploring.

I built VeloSpark to solve this. The app takes your preferences—distance, elevation, time, activity type—and surfaces routes that actually fit what you're looking for. It integrates with Strava for personalized recommendations based on your activity history.

The Stack

I went with a modern monorepo setup using Turborepo and pnpm. The frontend is Next.js 16 with React 19 and Tailwind CSS 4. Auth is handled by Auth.js v5 with Strava OAuth. The database is PostgreSQL 16 with Prisma 7 as the ORM. On the backend, I have a FastAPI service running the ML recommendation engine using scikit-learn and pandas.

Maps use OpenStreetMap with Leaflet—I wanted to avoid the Google Maps dependency and the OSM tiles look great.

Onboarding Flow

New users are guided through a 3-step questionnaire. This captures their experience level, terrain preferences, and typical ride/run duration. The answers influence the ML model's recommendations.

Experience Level Selection

Terrain Preference

Duration Preference

Route Finder

The route finder gives users precise control over their search. Sliders let you dial in distance, elevation gain, and time. The map lets you click to set a starting location.

Route Finder Interface

Search results display on an interactive map with route overlays. The list is paginated and each card shows the route name, distance, and elevation gain. There's also a "Popular nearby" section that surfaces routes frequently ridden by other athletes in the area.

Route Results

Route Details

Clicking into a route shows the full GPS trace on a map. A slide-out panel displays route statistics and a GPS tracking toggle.

Route Map View

Route Detail Panel

Strava Integration

Authentication is handled via Strava OAuth. I used Auth.js v5 which made the integration straightforward.

VeloSpark Sign-in

Strava OAuth

Once authenticated, the app greets you by name and remembers your preferences.

Authenticated Homepage

User Profile and Settings

The profile page shows your account info and lets you set your default activity type.

Profile Page

The settings page is where things get interesting. Beyond the standard appearance and privacy toggles, there's a surface preference setting (pavement, mixed, or dirt) that influences recommendations at the ML model level. You can also connect Garmin, Wahoo, or RideWithGPS to export routes directly to your GPS device.

Settings Page

What's Next

I'm planning to add route creation tools, social features for following athletes and sharing routes, weather-aware recommendations, and segment analysis with PR predictions.

Cheers - Gordon