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

rovemaps-you-are-here

v2.5.0

Published

A Three.js and MapLibre GL JS SDK for displaying user location with GPS accuracy and direction indicators

Readme


Overview

RoveMaps YouAreHere provides a production-ready "blue dot" user location marker for mapping applications. Drop it into any Three.js scene or MapLibre GL map and get a polished, Google Maps-style location indicator with GPS accuracy visualization and smooth animations.

import { ThreeYouAreHereController } from 'rovemaps-you-are-here';

const controller = new ThreeYouAreHereController({
  center: [-74.006, 40.7128], // NYC
});

await controller.start(scene);

Try the Live Demo →


Features

| Feature | Description | |---------|-------------| | Blue Dot Marker | Clean, Google Maps-style user location indicator | | Accuracy Ring | Pulsing ring that scales with GPS precision | | Direction Cone | Heading indicator when user is moving | | Compass Support | Device orientation for heading when stationary | | Confidence States | Visual feedback for GPS signal quality | | Smooth Animations | Frame-rate independent position and heading interpolation | | Auto Staleness | Automatic confidence degradation when GPS signal is lost | | Battery Aware | Pauses tracking when tab is hidden, smart resume when visible | | Multi-Platform | Three.js, MapLibre GL JS, and MapBox GL JS support |


Architecture

┌─────────────────────────────────────────────────────────────────┐
│                        Your Application                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌──────────────────────┐    ┌──────────────────────┐          │
│  │  ThreeYouAreHere     │    │  MapLibreYouAreHere  │          │
│  │  Controller          │    │  Controller          │          │
│  └──────────┬───────────┘    └──────────┬───────────┘          │
│             │                           │                        │
│             └─────────┬─────────────────┘                        │
│                       │                                          │
│             ┌─────────▼─────────┐                               │
│             │ GeolocationProvider│ ◄── Shared location source    │
│             └─────────┬─────────┘                               │
│                       │                                          │
│  ┌────────────────────┼────────────────────┐                    │
│  │                    │                    │                    │
│  ▼                    ▼                    ▼                    │
│ GPS API          Compass API        Visibility API              │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Key Components:

  • Controllers — High-level API that manages marker + geolocation together
  • Markers — Visual components (Three.js meshes or MapLibre custom layers)
  • GeolocationProvider — Unified location source with events and permissions
  • MercatorProjection — GPS → scene coordinate conversion

Installation

npm (Recommended)

npm install rovemaps-you-are-here

CDN

<!-- Three.js (peer dependency) -->
<script src="https://unpkg.com/[email protected]/build/three.min.js"></script>

<!-- RoveMaps YouAreHere -->
<script src="https://unpkg.com/[email protected]/dist/rovemaps-you-are-here.umd.cjs"></script>

<script>
  const { ThreeYouAreHereController } = RoveMapsYouAreHere;
</script>

| CDN | URL | |-----|-----| | unpkg | https://unpkg.com/[email protected]/dist/rovemaps-you-are-here.umd.cjs | | jsDelivr | https://cdn.jsdelivr.net/npm/[email protected]/dist/rovemaps-you-are-here.umd.cjs |


Quick Start

Three.js

import * as THREE from 'three';
import { ThreeYouAreHereController } from 'rovemaps-you-are-here';

// Create your scene
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight);
const renderer = new THREE.WebGLRenderer();

// Initialize the controller
const controller = new ThreeYouAreHereController({
  center: [-74.006, 40.7128], // [longitude, latitude] of your map center
  scale: 1,
  markerOptions: {
    color: 0x4285F4,
    showAccuracyRing: true,
    showDirectionCone: true,
  },
  onUpdate: (location) => {
    console.log('Location:', location.latitude, location.longitude);
  },
  onError: (error) => {
    console.error('GPS Error:', error.message);
  },
});

// Start tracking (requests GPS permission)
await controller.start(scene);

// Animation loop
function animate() {
  requestAnimationFrame(animate);
  renderer.render(scene, camera);
}
animate();

// Cleanup when done
controller.dispose();

MapLibre GL JS

import maplibregl from 'maplibre-gl';
import { MapLibreYouAreHereController } from 'rovemaps-you-are-here';

const map = new maplibregl.Map({
  container: 'map',
  style: 'https://demotiles.maplibre.org/style.json',
  center: [-74.006, 40.7128],
  zoom: 14,
});

const controller = new MapLibreYouAreHereController({
  markerOptions: { color: 0x4285F4 },
  flyToOnFirstFix: true,
  flyToZoom: 16,
});

map.on('load', async () => {
  await controller.start(map);
});

React + React Three Fiber

import { Canvas, useFrame, useThree } from '@react-three/fiber';
import { useYouAreHere } from 'rovemaps-you-are-here';

function YouAreHereMarker() {
  const { camera } = useThree();

  const { marker, update, start } = useYouAreHere({
    center: [-74.006, 40.7128], // NYC
    autoStart: true,
    markerOptions: {
      color: 0x4285f4,
      showAccuracyRing: true,
    },
  });

  useFrame((_, delta) => {
    update(delta, camera);
  });

  return <primitive object={marker} />;
}

function App() {
  return (
    <Canvas camera={{ position: [0, 200, 200] }}>
      <ambientLight />
      <YouAreHereMarker />
    </Canvas>
  );
}

Available React Hooks:

| Hook | Description | |------|-------------| | useYouAreHere | Full marker + geolocation integration for Three.js | | useLocation | Standalone geolocation hook (no marker) |

See examples/react for a complete working example.


API Reference

ThreeYouAreHereController

The all-in-one controller for Three.js applications.

const controller = new ThreeYouAreHereController(options);

Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | center | [number, number] | Required | Map center as [longitude, latitude] | | scale | number | 1 | Coordinate scale factor | | markerOptions | UserMarkerOptions | {} | Visual appearance options | | geolocationOptions | GeolocationOptions | {} | GPS configuration | | enableCompass | boolean | true | Enable device compass | | onUpdate | (location) => void | — | Location update callback | | onError | (error) => void | — | Error callback | | onPermissionChange | (state) => void | — | Permission state callback |

Methods

| Method | Description | |--------|-------------| | start(scene) | Add marker to scene and start GPS tracking | | stop() | Stop tracking and remove marker | | dispose() | Clean up all resources | | requestPermissions() | Request compass permission (iOS) | | isActive() | Check if controller is running |

Properties

| Property | Type | Description | |----------|------|-------------| | marker | ThreeUserMarker | Direct access to the marker | | geolocation | GeolocationProvider | Direct access to location provider |


MapLibreYouAreHereController

The all-in-one controller for MapLibre GL JS.

const controller = new MapLibreYouAreHereController(options);

Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | markerOptions | UserMarkerOptions | {} | Visual appearance options | | geolocationOptions | GeolocationOptions | {} | GPS configuration | | enableCompass | boolean | true | Enable device compass | | flyToOnFirstFix | boolean | true | Auto-fly to user on first GPS fix | | flyToZoom | number | 16 | Zoom level for fly-to | | onUpdate | (location) => void | — | Location update callback | | onError | (error) => void | — | Error callback | | onPermissionChange | (state) => void | — | Permission state callback |

Methods

| Method | Description | |--------|-------------| | start(map) | Add marker to map and start GPS tracking | | stop() | Stop tracking and remove marker | | dispose() | Clean up all resources | | flyToUser(options?) | Fly camera to user location |


UserMarkerOptions

Visual configuration for the marker.

interface UserMarkerOptions {
  // Colors (hex values)
  color?: number;              // Main dot color (default: 0x4285F4)
  borderColor?: number;        // Border color (default: 0xFFFFFF)
  coneColor?: number;          // Direction cone color
  accuracyRingColor?: number;  // Accuracy ring color

  // Sizes (scene units)
  dotSize?: number;            // Dot radius (default: 9)
  borderWidth?: number;        // Border width (default: 3)
  coneLength?: number;         // Cone length (default: 45)
  coneWidth?: number;          // Cone width (default: 70)

  // Visibility
  showAccuracyRing?: boolean;  // Show pulsing ring (default: true)
  showDirectionCone?: boolean; // Show heading cone (default: true)

  // Animation
  pulseSpeed?: number;         // Ring pulse speed (default: 0.3)
  smoothPosition?: boolean;    // Smooth position updates (default: true)
  smoothHeading?: boolean;     // Smooth heading updates (default: true)

  // Behavior
  minSpeedForDirection?: number;  // Min speed to show cone (default: 0.5 m/s)
  orientation?: 'z-up' | 'y-up';  // Coordinate system (default: 'z-up')
}

GeolocationProvider

Low-level location provider with event-based updates.

const geo = new GeolocationProvider(options);

geo.on('update', (location) => { /* ... */ });
geo.on('error', (error) => { /* ... */ });
geo.on('permissionChange', (state) => { /* ... */ });

await geo.start();

Methods

| Method | Description | |--------|-------------| | start() | Start watching location | | stop() | Stop watching | | dispose() | Clean up resources | | getCurrentPosition() | Get single position fix | | getLastLocation() | Get cached last location | | getPermissionState() | Get current permission state | | isWatching() | Check if actively watching |

Mock Mode (Development)

// Set a single mock location
geo.setMockLocation({
  longitude: -74.006,
  latitude: 40.7128,
  accuracy: 10,
  heading: 90,
  speed: 1.5,
});

// Simulate movement along a path
const stop = geo.startMockPath([
  { longitude: -74.006, latitude: 40.7128 },
  { longitude: -74.005, latitude: 40.7130 },
  { longitude: -74.004, latitude: 40.7132 },
], 1000); // 1 second between points

// Stop simulation
stop();

Note: Mock mode is automatically disabled when productionMode: true is set.


Confidence States

Visual feedback for GPS signal quality.

type ConfidenceState = 'high' | 'low' | 'lost' | 'warning' | 'danger';

// Manual control
controller.marker.setConfidence('low');

// Automatic (default behavior)
// - 'low' after 30s without update or accuracy > 100m
// - 'lost' after 60s without update or accuracy > 500m

| State | Appearance | Trigger | |-------|------------|---------| | high | Blue dot, normal pulse | Good GPS signal | | low | Blue dot, reduced opacity | 30s stale or >100m accuracy | | lost | Grey dot, growing ring | 60s stale or >500m accuracy | | warning | Orange dot, fast pulse | Manual trigger (geofence, etc.) | | danger | Red dot, fastest pulse | Manual trigger (emergency) |


Error Handling

The SDK uses structured errors with machine-readable codes.

import { RoveError, RoveErrorCode } from 'rovemaps-you-are-here';

controller.geolocation.on('error', (error) => {
  if (RoveError.is(error, RoveErrorCode.PERMISSION_DENIED)) {
    showPermissionPrompt();
  } else if (RoveError.is(error, RoveErrorCode.GPS_SIGNAL_LOST)) {
    showSignalLostBanner();
  }
});

Error Codes

| Code | Description | |------|-------------| | PERMISSION_DENIED | User denied location permission | | GPS_SIGNAL_LOST | Location temporarily unavailable | | TIMEOUT | Location request timed out | | GEOLOCATION_UNSUPPORTED | Browser doesn't support geolocation | | INSECURE_CONTEXT | HTTPS required for geolocation | | INVALID_COORDINATES | Invalid latitude/longitude values |


Examples

Custom Colors

const controller = new ThreeYouAreHereController({
  center: [-74.006, 40.7128],
  markerOptions: {
    color: 0xFF6B6B,           // Coral red
    borderColor: 0xFFFFFF,
    accuracyRingColor: 0xFF6B6B,
    coneColor: 0xFF6B6B,
  },
});

Disable Animations (Low Power Mode)

const controller = new ThreeYouAreHereController({
  center: [-74.006, 40.7128],
  markerOptions: {
    smoothPosition: false,
    smoothHeading: false,
    pulseSpeed: 0,              // Disable pulse animation
    showAccuracyRing: false,    // Hide ring entirely
  },
});

Custom Coordinate System (Y-Up)

// Standard Three.js Y-up scene
const controller = new ThreeYouAreHereController({
  center: [-74.006, 40.7128],
  markerOptions: {
    orientation: 'y-up',
  },
});

React Integration

import { useEffect, useRef } from 'react';
import { ThreeYouAreHereController } from 'rovemaps-you-are-here';

function LocationMarker({ scene, center }) {
  const controllerRef = useRef<ThreeYouAreHereController | null>(null);

  useEffect(() => {
    const controller = new ThreeYouAreHereController({ center });
    controllerRef.current = controller;

    controller.start(scene).catch(console.error);

    return () => {
      controller.dispose();
    };
  }, [scene, center]);

  return null;
}

Project Structure

rovemaps-you-are-here/
├── src/
│   ├── lib/
│   │   ├── index.ts              # Main exports
│   │   ├── types.ts              # TypeScript interfaces
│   │   ├── errors.ts             # Error codes and RoveError class
│   │   ├── sources.ts            # LocationSource interface
│   │   ├── GeolocationProvider.ts
│   │   ├── three/
│   │   │   ├── ThreeUserMarker.ts
│   │   │   └── ThreeYouAreHereController.ts
│   │   ├── maplibre/
│   │   │   ├── MapLibreUserMarker.ts
│   │   │   └── MapLibreYouAreHereController.ts
│   │   ├── mapbox/
│   │   │   ├── MapBoxUserMarker.ts
│   │   │   └── MapBoxYouAreHereController.ts
│   │   └── svelte/
│   │       └── index.ts          # Svelte store helpers
│   └── utils/
│       ├── MercatorProjection.ts
│       └── validation.ts
├── examples/
│   ├── cdn-example.html
│   └── fleet-tracking.html
├── dist/                         # Built output
├── docs/
│   └── plans/
└── tests/

Development

Prerequisites

  • Node.js 18+
  • npm 9+

Setup

git clone https://github.com/russellmiddleton33/RoveBeacon.git
cd RoveBeacon
npm install

Commands

| Command | Description | |---------|-------------| | npm run dev | Start dev server with hot reload | | npm run build | Build demo application | | npm run build:lib | Build library for npm | | npm run check | Run TypeScript type checks | | npm test | Run unit tests | | npm test -- --coverage | Run tests with coverage |

Testing

# Run all tests
npm test

# Run tests in watch mode
npm test -- --watch

# Run specific test file
npm test -- GeolocationProvider

Browser Support

| Browser | Version | Notes | |---------|---------|-------| | Chrome | 80+ | Full support | | Firefox | 78+ | Full support | | Safari | 14+ | Requires HTTPS; compass needs user gesture | | Edge | 80+ | Full support | | iOS Safari | 14+ | Requires HTTPS; compass permission prompt | | Chrome Android | 80+ | Full support |

Important: Geolocation requires HTTPS in production. localhost is allowed for development.


Performance Tips

Mobile Optimization

// For mobile devices, consider reducing visual complexity
const controller = new ThreeYouAreHereController({
  center: [-74.006, 40.7128],
  markerOptions: {
    pulseSpeed: 0.15,           // Slower pulse
    showDirectionCone: false,   // Hide cone to reduce draw calls
  },
  geolocationOptions: {
    enableHighAccuracy: true,   // Better accuracy but more battery
    timeout: 15000,             // Longer timeout for weak signals
  },
});

Memory Management

Always call dispose() when removing the marker:

// Cleanup on unmount
controller.dispose();

Troubleshooting

"Permission denied" on iOS

iOS requires a user gesture to request location permission. Wrap start() in a click handler:

button.addEventListener('click', async () => {
  await controller.requestPermissions(); // Compass permission
  await controller.start(scene);         // Location permission
});

Marker not showing

  1. Ensure GPS permission is granted
  2. Check that center coordinates are valid
  3. Verify the marker is within camera view
  4. Check console for error messages

Jittery compass heading

Increase the heading smoothing factor:

markerOptions: {
  headingSmoothingFactor: 0.08, // Lower = smoother (default: 0.15)
}

Contributing

We welcome contributions! Please see our Contributing Guide for details.

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

MIT © Russell Middleton