Le vent tourne. — Wind-optimised road cycling route planner, built as a mobile-first PWA.
souplesse Ultra generates GPS round-trip cycling routes optimised for tailwind on the return leg. It fetches hourly weather forecasts from Open-Meteo for your planned start time, scores four candidate loop directions by wind alignment, and returns the best match via OpenRouteService. Each result includes an elevation profile, nutrition estimate (water, gels, bars, calories), and contextual ride tips. Routes export as GPX files compatible with Garmin, Wahoo, and Komoot.
The app is free, runs entirely in the browser, and works offline after first load as a PWA.
Production: souplesse-ultra.vercel.app
| Layer | Technology |
|---|---|
| Framework | SvelteKit 2 + Svelte 5 (runes) |
| Language | TypeScript |
| Styling | Tailwind CSS v3 |
| Maps | MapLibre GL |
| Routing API | OpenRouteService |
| Weather API | Open-Meteo |
| PWA | @vite-pwa/sveltekit |
git clone https://github.com/moindnl/melinoe-engine.git
cd melinoe-engine
npm install
npm run dev
Open http://localhost:5173.
The ORS routing key can be provided two ways:
In-app (recommended): Enter the key directly in the app — stored in localStorage.
Build-time (for deployment): Create .env:
VITE_ORS_API_KEY=your_key_here
Without a key the app returns mock routes for UI testing.
src/
├── lib/
│ ├── components/
│ │ ├── BottomSheet.svelte # modal sheet with drag-to-close
│ │ ├── DateTimePicker.svelte # day + time selector
│ │ ├── ElevationChart.svelte # SVG elevation profile
│ │ ├── InstallPrompt.svelte # PWA install banner
│ │ └── MapView.svelte # MapLibre GL route display
│ └── services/
│ ├── routing.ts # ORS API, wind scoring, loop generation
│ ├── weather.ts # Open-Meteo hourly forecast lookup
│ ├── gpx.ts # GPX export with <ele> tags
│ └── optimizer.ts # contextual ride tips
└── routes/
└── +page.svelte # single-page app shell
scripts/
├── gen-icons.mjs # generates PNG icons + OG image from SVGs
└── push-status.sh # polls Vercel deployment status after push
static/
├── icon.svg # 512×512 app icon (sine wave, liquid glass)
├── favicon.svg # 32×32 favicon
└── og-image.png # 1200×630 social share card
npm run build
npm run preview # preview production build locally
After editing static/icon.svg or static/og-image.svg, regenerate PNGs:
node scripts/gen-icons.mjs
Requires sharp (already in devDependencies).
| Variable | Description |
|---|---|
VITE_ORS_API_KEY |
OpenRouteService API key (optional — can be entered in-app) |
User preferences are persisted in localStorage (tb_settings). Active session state survives page refresh via sessionStorage (tb_session).
Copyright © 2026 Daniel Muschinski — all rights reserved. See LICENSE.