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

browser-car-speed

v0.1.0

Published

Reliable browser GPS speed and movement detection with a tiny typed API.

Readme

browser-car-speed

Tiny, typed browser GPS speed and movement detection.

npm install browser-car-speed

browser-car-speed reads the standard Geolocation API, prefers the browser's reported coords.speed, and uses a guarded rolling-coordinate estimate when that value is missing or stuck at zero. It has no runtime dependencies and does nothing until you call start().

Quick start

import {
  start,
  stop,
  subscribe,
  getCarSpeed,
  isCarMoving,
} from "browser-car-speed";

const unsubscribe = subscribe((snapshot) => {
  console.log(snapshot.status);                 // "locating" | "live" | ...
  console.log(snapshot.reading?.kilometersPerHour);
});

// Call from a click/tap because the browser may show a location prompt.
startButton.addEventListener("click", start);

getCarSpeed();        // km/h: number | null
getCarSpeed("mph");  // mph:  number | null
isCarMoving();        // boolean | null

unsubscribe();
stop();

null always means unknown or unavailable. It is never silently converted to zero. This distinction matters when GPS accuracy is too weak to prove that the device is stationary.

Independent trackers

Use createCarSpeed() when an app needs custom thresholds, an injected adapter, or more than one tracker.

import { createCarSpeed } from "browser-car-speed";

const car = createCarSpeed({
  movingThresholdKmh: 4,
  stationaryThresholdKmh: 2,
  staleAfterMs: 8_000,
});

const unsubscribe = car.subscribe(({ status, reading, moving }) => {
  if (status !== "live") return;
  console.log(reading.kilometersPerHour, moving);
});

car.start();

// Later:
unsubscribe();
car.stop();

API

Convenience singleton

  • start() / startCarSpeed() — begin high-accuracy location tracking.
  • stop() / stopCarSpeed() — clear the watcher, timer, and retained state.
  • getCarSpeed(unit?) — current full-precision speed in "km/h" (default), "mph", or "m/s"; null when unavailable or stale.
  • isCarMoving() — hysteresis-based true or false; null when unknown.
  • getSnapshot() / getCarSpeedSnapshot() — current state and metadata.
  • subscribe(listener, options?) / subscribeCarSpeed(...) — state updates; returns an idempotent unsubscribe function.
  • carSpeed — the shared tracker used by these functions.

Tracker

createCarSpeed(options?) returns:

interface CarSpeedTracker {
  start(): void;
  stop(): void;
  getCarSpeed(unit?: "km/h" | "mph" | "m/s"): number | null;
  isCarMoving(): boolean | null;
  getSnapshot(): CarSpeedSnapshot;
  subscribe(listener: CarSpeedListener, options?: { emitCurrent?: boolean }): () => void;
}

Snapshot

interface CarSpeedSnapshot {
  status: "idle" | "locating" | "live" | "stale" | "error" | "unsupported";
  reading: SpeedReading | null;      // fresh reading only
  lastReading: SpeedReading | null;  // retained while stale
  moving: boolean | null;
  error: CarSpeedError | null;
  running: boolean;
  supported: boolean;
  accuracyMeters: number | null;
  lastPositionTimestamp: number | null;
}

interface SpeedReading {
  metersPerSecond: number;
  kilometersPerHour: number;
  milesPerHour: number;
  source: "sensor" | "calculated";
  accuracyMeters: number | null;
  timestamp: number;
}

Snapshots and readings are immutable.

Options

| Option | Default | Purpose | | --- | ---: | --- | | movingThresholdKmh | 3 | Enter the moving state at or above this speed. | | stationaryThresholdKmh | 1.5 | Leave the moving state at or below this speed. | | staleAfterMs | 10000 | Stop exposing a reading after this age. | | maximumSpeedKmh | 260 | Reject implausibly high values. | | maximumFallbackAccuracyMeters | 15 | Accuracy ceiling for coordinate-derived speed. | | coordinateFallback | true | Estimate speed when coords.speed is unavailable. | | minimumFallbackIntervalMs | 500 | Shortest coordinate baseline. | | fallbackWindowMs | 20000 | Longest rolling coordinate baseline. | | fallbackMinimumDisplacementMeters | 3 | Minimum absolute displacement evidence. | | smoothingWindow | 3 | Accepted readings in the median filter (1–9). | | maximumAccelerationKmhPerSecond | 60 | Maximum filtered change per second. | | speedJumpToleranceKmh | 15 | Free jump tolerance before limiting changes. | | freshnessIntervalMs | 500 | Stale-state notification interval; 0 disables it. | | positionOptions | high accuracy, no cache, 15 s timeout | Native watch options. | | geolocation | browser API | Structural adapter for testing or hybrid runtimes. | | now | Date.now | Epoch-millisecond clock, injectable for tests. |

How fallback avoids common GPS mistakes

  • Displacement inside the reported accuracy radius remains unknown, not stationary.
  • A rolling window preserves evidence of slow movement instead of repeatedly resetting the baseline.
  • Two consistent, progressing coordinate estimates are required before fallback speed is accepted.
  • A fixed GPS jump is rejected because its displacement does not continue.
  • Old smoothing samples are reset after a stale gap.
  • A browser-provided zero can be overridden by sustained coordinate evidence.
  • Separate enter/exit thresholds keep movement state from flickering near zero.

Browser and vehicle limitations

This package estimates whether the browser/device is moving. It does not read Tesla CAN data, vehicle telemetry, wheel speed, or a calibrated dashboard sensor. Results are indicative and depend on the browser's Geolocation implementation, GPS conditions, hardware, permissions, and software version.

  • Use HTTPS (or localhost); browsers normally restrict Geolocation on insecure origins.
  • Ask for location from a user gesture where possible.
  • Do not use this package as a safety system or a replacement for the vehicle's speedometer.
  • Tesla fullscreen/Theater workarounds belong in the application layer and are deliberately not included here.

Utilities

import { convertSpeed, haversineMeters } from "browser-car-speed";

convertSpeed(100, "km/h", "mph");
haversineMeters(41.0082, 28.9784, 41.0151, 28.9795);

Development

npm install
npm run verify

The verification suite type-checks and builds the package, runs behavior tests, checks package exports/types, inspects the tarball allow-list, and installs the packed tarball into a clean temporary project.

License

MIT