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

@alosha/stride

v0.2.1

Published

Parse GPX, TCX and FIT files and compute running metrics — pace, HR zones, splits, and Chart.js dashboards. Zero config.

Readme

@alosha/stride

Parse GPX, TCX and FIT files, compute running metrics, and render Chart.js dashboards — zero config.

npm version npm downloads License: MIT

  • GPX, TCX & FIT in, insights out — parses GPX and TCX XML (file path or raw string) plus binary FIT files from Garmin, Strava, Coros, Wahoo and more. Format is auto-detected; the same analyze() and charts work for all three.
  • Every running metric you want — pace, splits, HR zones (Z1–Z5), elevation, cadence, moving vs. elapsed time.
  • Charts with zero boilerplate — 5 ready-made Chart.js configs (pace, elevation, heart rate, HR zones, splits).
  • CLI and librarynpx stride analyze run.gpx, metric or imperial, no config required.

Supported formats

Most running libraries handle GPX only. Stride reads the three formats your devices and platforms actually export, and normalises them into one shape — so analyze() and every chart config work identically regardless of source.

| Format | Extension | Input types | Typical sources | |---|---|---|---| | GPX | .gpx | file path, raw XML string | Strava, Apple Health/Watch routes, Komoot, most apps | | TCX | .tcx | file path, raw XML string | Garmin Connect, Strava, Wahoo, Zwift | | FIT | .fit | file path, Uint8Array, ArrayBuffer | Garmin, Coros, Wahoo, Suunto, Polar (native device files) |

The format is auto-detected — you call parse() and never branch on file type. All three surface the same fields where the source provides them: GPS track, elevation, heart rate, cadence and timestamps.

Install

npm install @alosha/stride

Quick start

import { parse, analyze, paceChartConfig, splitsChartConfig } from '@alosha/stride'
import { Chart } from 'chart.js/auto'

// Parse a GPX, TCX or FIT file — the format is detected automatically
const activity = parse('./my-run.gpx')   // or './my-run.tcx', './my-run.fit'

// Compute all running metrics
const stats = analyze(activity)
console.log(stats.distanceM, stats.avgPaceSecPerKm, stats.hrZones)

// Render a pace chart (Chart.js)
new Chart(document.getElementById('pace'), paceChartConfig(activity, stats))
new Chart(document.getElementById('splits'), splitsChartConfig(stats))

CLI

npx stride analyze my-run.gpx
npx stride analyze my-run.tcx            # TCX and FIT work too — auto-detected
npx stride analyze my-run.fit
npx stride analyze my-run.fit --imperial

Output:

🏃 @alosha/stride — Morning Run

  Distance:      10.24 km
  Moving time:   51:32
  Elapsed time:  52:14
  Avg pace:      5:02/km
  Best km pace:  4:44/km
  Elevation ↑:   142m
  Elevation ↓:   138m
  Avg HR:        158 bpm
  Max HR:        178 bpm

  Splits:
    km  1  4:55/km  ↑12m  HR 152bpm
    km  2  5:03/km  ↑8m   HR 156bpm
    ...

API

parse(input: string | Uint8Array | ArrayBuffer): Activity

Parse an activity file into a normalised Activity object. The format is auto-detected, so the returned shape is identical for GPX, TCX and FIT.

// GPX / TCX — file path or raw XML string
const activity = parse('./run.gpx')
const activity = parse('./run.tcx')
const activity = parse(xmlString)

// FIT — file path (Node) or raw bytes (browser / streamed)
const activity = parse('./run.fit')
const activity = parse(new Uint8Array(arrayBuffer))

| Input | Detected as | |---|---| | String/contents containing <gpx | GPX | | String/contents containing <TrainingCenterDatabase | TCX | | File path to a .FIT file, or Uint8Array / ArrayBuffer bytes | FIT |

In the browser, file paths aren't available — read the file with a FileReader and pass the result to parse() (a string for GPX/TCX via readAsText, or a Uint8Array for FIT via readAsArrayBuffer).

Per-format details

Every format produces the same Activity object, but each has a few quirks worth knowing:

GPX (.gpx) — reads <trkpt> lat/lon, <ele>, <time>, and the Garmin TrackPointExtension for heart rate and cadence (gpxtpx:/ns3: namespaces). Cadence is doubled from per-foot RPM to steps/min.

TCX (.tcx) — Garmin Training Center XML. All Activity → Lap → Track → Trackpoint elements are flattened into one continuous point stream, so multi-lap files just work. Reads Position (LatitudeDegrees/LongitudeDegrees), AltitudeMeters, HeartRateBpm, and the activity-extension RunCadence (namespaced or bare), doubled to steps/min. The Sport attribute becomes activity.type. Trackpoints without a Position (indoor/paused) are skipped.

FIT (.fit) — binary device files, decoded with @garmin/fitsdk. Positions are converted from semicircles to degrees (× 180 / 2³¹), the higher-resolution enhancedAltitude is preferred over altitude when present, and cadence (plus fractionalCadence) is normalised to steps/min. Sport and start time come from the session/sport messages. Records without GPS are skipped. Pass FIT as a file path (Node) or as Uint8Array/ArrayBuffer bytes (browser).

analyze(activity: Activity, maxHR?: number): ActivityStats

Compute all metrics from an activity. maxHR defaults to 190 and is used for heart rate zone calculation.

const stats = analyze(activity)
// stats.distanceM, stats.avgPaceSecPerKm, stats.splits, stats.hrZones, ...

Chart configs

All chart functions return a plain Chart.js configuration object — you instantiate Chart yourself.

| Function | Chart type | Data needed | |---|---|---| | paceChartConfig(activity, stats, opts?) | Line | Always | | elevationChartConfig(activity, stats, opts?) | Line | Elevation in GPX | | heartRateChartConfig(activity, stats) | Line | HR in GPX | | hrZonesChartConfig(stats) | Doughnut | HR in GPX | | splitsChartConfig(stats, opts?) | Bar | Always |

import { Chart } from 'chart.js/auto'
import { paceChartConfig, elevationChartConfig, hrZonesChartConfig } from '@alosha/stride'

new Chart(canvas1, paceChartConfig(activity, stats, { units: 'imperial' }))
new Chart(canvas2, elevationChartConfig(activity, stats))
new Chart(canvas3, hrZonesChartConfig(stats))

Formatting helpers

import { formatPace, formatDistance, formatDuration } from '@alosha/stride'

formatPace(302, 'metric')    // "5:02/km"
formatPace(302, 'imperial')  // "8:06/mi"
formatDistance(10240)        // "10.24 km"
formatDuration(3092)         // "51:32"

ActivityStats reference

| Field | Type | Description | |---|---|---| | distanceM | number | Total distance in metres | | elapsedTimeSec | number | Total elapsed time in seconds | | movingTimeSec | number | Moving time (pauses excluded) | | avgPaceSecPerKm | number | Average pace in sec/km | | bestKmPaceSecPerKm | number \| null | Fastest 1km split | | elevationGainM | number | Total elevation gain in metres | | elevationLossM | number | Total elevation loss in metres | | avgHeartRate | number \| null | Average HR in bpm | | maxHeartRate | number \| null | Max HR in bpm | | hrZones | HeartRateZones \| null | Time in each HR zone (seconds) | | avgCadence | number \| null | Average cadence in steps/min | | splits | Split[] | Per-km splits |


Want hosted activity history, training plans, and team dashboards? → stride.alosha.dev

Built by Alosha