npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@glandais/vcyclist-engine

v6.9.1

Published

[![npm engine](https://img.shields.io/npm/v/@glandais/vcyclist-engine?label=%40glandais%2Fvcyclist-engine)](https://www.npmjs.com/package/@glandais/vcyclist-engine) [![npm elevation](https://img.shields.io/npm/v/@glandais/vcyclist-elevation?label=%40gland

Readme

vcyclist

npm engine npm elevation Maven Central Maven Central elevation Maven Central gpx Maven Central fit Maven Central map

Kotlin Multiplatform physics-based cycling simulator: it turns a static GPS trace into a virtualized ride with realistic speeds, times and power estimates. Elevation data comes from DEM tiles — IGN LiDAR HD in France by default, Mapterhorn elsewhere and as its fallback — fetched and decoded by the :elevation module.

                        ┌──────────────┐
        sample.gpx ────▶│ GpxParser    │
                        └──────┬───────┘
                               ▼
            ┌─────────────────────────────────────────┐
            │  Enhancer (orchestrator)                │
            │  ├─ PointPerDistance(-1, 30)            │
            │  ├─ fixElevation (Terrarium tiles)*     │
            │  ├─ PointPerDistance(1, 2)              │
            │  ├─ bridgeDecks (ign-lidar-hd z15)*     │
            │  ├─ tunnels (any DEM source)*           │
            │  ├─ smoothElevation (150 m kernel)      │
            │  ├─ PathCurvature (turn radius)         │
            │  │   or RacingLine (optimal line)*      │
            │  ├─ MaxSpeedComputer (cornering+braking)│
            │  ├─ VirtualizeService (1 Hz physics)    │
            │  ├─ PointPerSecond (uniform sampling)   │
            │  ├─ W′bal (Critical Power annotation)   │
            │  ├─ ElevationGain (D+/D− dead band)     │
            │  └─ PathSimplifier (Douglas-Peucker 3D) │
            └──────────────────┬──────────────────────┘
                               ▼
                        ┌──────────────┐
                        │ GpxWriter    │────▶ output.gpx
                        └──────────────┘
            (*) optional — needs an ElevationProvider (bridgeDecks:
                one carrying a surface model; tunnels: any)

What it can do

In: GPX tracks, routes (<rte>), segments and waypoints.

Physics: elevation correction from DEM tiles, curvature estimation or an optimal racing line, cornering and braking sharing one friction-ellipse budget, a configurable rider and bike, and a 1 Hz time-stepping simulation that produces speed, time and power at every point.

Out: GPX, Garmin FIT courses, CSV, column-oriented JSON, static PNG maps — plus climb detection, a racing-line report and worst-case wind analysis.

Four doors reach the same engine, and a capability is available from all of them unless noted:

| Capability | CLI | Kotlin / Java | JavaScript / TS | WASI | |---|---|---|---|---| | Run the pipeline | enhance | Enhancer.enhanceCourseDefault | enhance | vcEnhance | | Configure rider, bike, wind, power | --cyclist-*, --bike-*, --wind-* | CoursePhysics(Course(…)) | enhanceWithCourse | vcEnhanceWithCourse | | Power models constant · durability · critical-power · from_data | --cyclist-model | CyclistPowerSpec | power.type | power.type | | Terrain pacing, power slew limit | --cyclist-pacing, --cyclist-slew | provider decorators | power.pacing, power.maxSlewWPerS | idem | | Road condition, dry or wet | --road-condition | Cyclist.withRoadCondition | cyclist.roadCondition | idem | | Pedal-strike clearance | --bike-max-pedal-angle | Bike.maxPedalingLeanAngleDeg | bike.maxPedalingLeanAngleDeg | idem | | Racing line + corridor mode | --racing-line, --corridor | RacingLineOptions | racingLineEnabled | idem | | DEM elevation correction | --fix-elevation | ElevationProvider | fixElevation: true | host serves tiles | | Climb detection | — | ClimbDetector.detect | detectClimbs | vcDetectClimbsJson | | Racing-line report | --racing-line-report | RacingLine.analyze | analyzeRacingLine | vcAnalyzeRacingLineJson | | Write GPX / CSV / JSON | --gpx --csv --json | GpxWriter, CsvWriter, JsonWriter | writeGpx, pathToCsv, pathToJson | vcWriteGpx, vcPathToCsv/Json | | Write FIT course | --fit | Path.toFitBytes | pathToFit, pathsToFit | vcPathToFit, vcPathsToFit | | Static map PNG | export --map | MapFactoriesJvm | — | — |

Static maps are JVM-only by construction — :map draws on java.awt. docs/ledgers/surface-coverage.md tracks this matrix as capabilities land, so that a feature cannot reach one door and quietly miss the others.

Install

npm — browser, Node.js and Bun

npm install @glandais/vcyclist-engine          # physics, GPX, FIT, CSV/JSON, climbs, racing line
npm install @glandais/vcyclist-elevation       # DEM lookups on their own

The engine bundle already carries the elevation façade; install the second package only if you want DEM lookups without the physics.

Gradle — JVM or Kotlin Multiplatform

dependencies {
    implementation("io.github.glandais:vcyclist-engine:4.2.1")     // pulls -jvm / -js per target
    implementation("io.github.glandais:vcyclist-elevation:4.2.1")
}

Maven

<dependency>
  <groupId>io.github.glandais</groupId>
  <artifactId>vcyclist-engine-jvm</artifactId>
  <version>4.2.1</version>
</dependency>

The badges above are the source of truth for the current version. KMP consumers get the platform-specific variant automatically; from plain Maven, name the -jvm artifact yourself.

vcyclist-gpx (the Path model + GPX I/O) comes in transitively via vcyclist-engine, and can be depended on alone if you only need parsing and resampling. vcyclist-fit and vcyclist-map are published separately.

CLI and .wasm

The CLI is an application, not a library, so it is not on Maven Central: download vcyclist-cli-<version>-all.jar from a GitHub release. The WASI module is built from source (below) rather than published to a registry.

Requirements: Java 21+ for the JVM and CLI; Node ≥ 18 (22+ recommended) or Bun for JavaScript; a WASI runtime with the function-references, gc and exceptions proposals — wasmtime 46+ is known good.

Quick start

Command line

java -jar vcyclist-cli-*-all.jar enhance route.gpx --gpx out.gpx --csv out.csv

enhance runs the physics pipeline; export produces maps, FIT, CSV and JSON from a file you already have. Elevation correction is off unless you pass --fix-elevation, so nothing touches the network by default.

Full option reference, the rider models and what each is measured to be worth, and exit codes: cli/README.md.

Kotlin

import io.github.glandais.engine.Enhancer
import io.github.glandais.engine.gpx.GpxParser
import io.github.glandais.engine.gpx.GpxWriter
import io.github.glandais.engine.gpx.firstTrackAsPath
import io.github.glandais.engine.gpx.toGpxDocument

suspend fun virtualize(xml: String): String {
    val path = GpxParser.parse(xml).firstTrackAsPath()
    val out = Enhancer.enhanceCourseDefault(path)   // pure physics, no HTTP
    return GpxWriter.write(out.toGpxDocument(trackName = "virtualized"))
}

Pass an ElevationProvider as the second argument to correct elevations from DEM tiles, and an EnhanceOptions as the third to configure the pipeline. For a configured rider, build a CoursePhysics(Course(path, cyclist, bike), …) and call Enhancer.enhanceCourse.

Java

Path input = GpxToPathJvm.firstTrackAsPath(GpxParserJvm.parse(xml));
Path enhanced = EnhancerJvm.enhanceCourseDefaultBlocking(input);
String out = GpxWriterJvm.write(enhanced);

Every entry point has a …Jvm twin that restores Kotlin's default arguments, and every suspend function has both a …Blocking and a …Async (CompletableFuture) bridge. docs/guides/using-from-java.md has the calling rules — some of them matter, …Blocking on a UI thread being the obvious one.

JavaScript / TypeScript

Kotlin/JS emits a UMD bundle that preserves the package namespace, so there is exactly one top-level export. Named imports do not work — unwrap it once:

import * as engineRaw from '@glandais/vcyclist-engine';
const engine = engineRaw.io.github.glandais.engine;

const { parseGpx, enhance, writeGpx, pathSize, pathTotalDistance } = engine;

const path = parseGpx(gpxXml);
const out  = await enhance(path, null);          // physics only; { fixElevation: true } for DEM
console.log(pathSize(out), pathTotalDistance(out), 'm');

// `<power>` carries the SOURCE file's power by default — writing simulated data into a format the
// ecosystem reads as a recording is the caller's call. Ask for the simulation explicitly:
const xml  = writeGpx(out, true, 'computed-or-input', 'my route');

docs/guides/using-from-javascript.md covers the whole façade — enhanceWithCourse and its five DTOs, FIT and CSV/JSON export, climbs, the racing-line report, the standalone elevation API, and the Node/Bun specifics.

WASI — no JVM, no JavaScript

:engine links a standalone WASI module, so the whole pipeline runs inside wasmtime, WasmEdge, wazero, or an embedding in Go, Rust, Python or the JVM.

./gradlew :engine:wasmModule       # -> engine/build/wasm/vcyclist-engine.wasm + .sha256

The host implements three imports — read_input, write_output and fetch_tile (which may simply answer "no tile") — and everything else is numeric exports over integer handles:

staged["bytes"] = open("ride.gpx", "rb").read()
handle = exports["vcParseGpx"](store, len(staged["bytes"]))

staged["bytes"] = b'{"computeOnePointPerSecond": true}'
out = exports["vcEnhance"](store, handle, len(staged["bytes"]))
print(exports["vcPathDurationMs"](store, out) / 1000, "s")

This is not a reduced surface: vcEnhanceWithCourse, vcPathToFit, vcPathToCsv, vcDetectClimbsJson and vcAnalyzeRacingLineJson are all there, and §10 of the guide is a function-by-function parity table against the JavaScript façade. docs/guides/wasm-wasi-abi.md is the full contract; tools/wasi is a working host that CI runs on every pull request.

Try the demo

https://glandais.github.io/vcyclist — no install, runs the real engine in your browser.

A Vue 3 + Leaflet + Chart.js app with two routes, both on the same Kotlin/JS bundle:

  • #/ — GPX analysis: upload a route, run the physics pipeline, inspect every field on a synchronized chart and map, with climb detection and the racing line, then download the result as GPX or as a Garmin FIT course.
  • #/elevation — elevation explorer: query DEM tiles at a point or along a path, with smoothing, Douglas-Peucker simplification and hillshade/slope relief.
cd demo && npm run dev        # http://localhost:3000, against a locally built engine

See demo/README.md for the architecture and the static-site build.

Documentation

docs/README.md is the index and says which documents are current and which are frozen history. The short version:

  • docs/guides/ — how to use and extend the project: Java, JavaScript, the WASI ABI, the racing line, the release flow
  • docs/ledgers/ — living state: research improvements, build warnings, surface coverage
  • docs/research/ — the solo-rider simulation research report
  • Module documentation lives next to its module: cli/, elevation/, map/, demo/

Contributing

Open PRs against develop — the default and only long-lived branch — using Conventional Commits. Build commands, module layout, testing conventions and the release flow are in CONTRIBUTING.md.

License

Apache License 2.0.