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

webxr-geospatial

v1.0.1

Published

Android companion localization and WGS84 coordinate utilities for WebXR

Readme

webxr-geospatial

TypeScript utilities for using an Android-provided ARCore Geospatial pose in a WebXR application.

English · Deutsch

English

webxr-geospatial connects a browser application with a compatible Android companion app and converts WGS84 positions into heading-aligned local coordinates suitable for a WebXR scene.

The package:

  • launches the companion app and supplies a browser return URL;
  • reads and validates the returned Geospatial pose;
  • removes localization data from the visible URL;
  • converts WGS84 coordinates through ECEF and ENU into local metre-based coordinates;
  • applies yaw from the ARCore East-Up-South quaternion while keeping the vertical axis upright;
  • derives a framework-independent yaw-only scene alignment from an initial WebXR viewer pose.

It is framework-independent, ships as an ES module with TypeScript declarations, and has no runtime dependencies.

Companion app

The localization flow is designed for use with the WebXR Geospatial Companion Android app. The app obtains the ARCore Geospatial pose and returns it to the browser. It is not included in this npm package and is not linked here because no public app repository is available yet.

The coordinate conversion utilities can also be used independently when a compatible GeospatialPose is available from another source.

Installation

npm install webxr-geospatial

Quick start

Register the pose handler when the page loads. Start localization from a user interaction so the browser is allowed to open the Android app.

import { createLocalCoordinateTransformer, handleGeospatialPose, startGeospatialLocalization } from "webxr-geospatial";

handleGeospatialPose((pose) => {
  const transformer = createLocalCoordinateTransformer(pose);

  // GeoJSON order: longitude, latitude, ellipsoidal altitude
  const [x, y, z] = transformer.toLocalPosition([13.52584808, 52.45678137, 74.189]);

  console.log("Local position:", { x, y, z });
});

document.querySelector("#locate")?.addEventListener("click", () => {
  startGeospatialLocalization();
});

The returned coordinates are relative to the Geospatial pose. The consuming application remains responsible for placing this local coordinate system into its actual WebXR reference space, for example by aligning it with the first WebXR viewer pose.

Public API

startGeospatialLocalization(options?)

Opens the Android companion app and supplies the URL to which it should return. Call it directly from a user interaction.

startGeospatialLocalization();

All configuration values are optional:

startGeospatialLocalization({
  returnUrl: window.location.href,
  companionAppLink: "https://example.com/android/locate/",
  companionPackageId: "com.example.companion",
  installFallbackUrl: "https://example.com/android/install/",
});

| Option | Purpose | | -------------------- | ------------------------------------------------------------------------------------------------------------- | | returnUrl | Browser URL reopened after localization. Defaults to the current URL. | | companionAppLink | HTTPS App Link handled by the companion app. | | companionPackageId | Android package ID used to target the launch intent. | | installFallbackUrl | Page opened when the companion app cannot be launched. Defaults to /android/install/ on the current origin. |

Return URLs must use HTTPS. HTTP is accepted only for loopback hosts during local development.

handleGeospatialPose(callback)

Synchronously reads a returned pose from the URL fragment, removes the localization fragment, validates the payload, and invokes the callback when the pose is valid.

handleGeospatialPose((pose) => {
  console.log(pose.latitude, pose.longitude, pose.altitude);
});

The callback receives:

interface GeospatialPose {
  latitude: number;
  longitude: number;
  altitude: number;
  quaternion: readonly [number, number, number, number];
}

altitude is measured in metres above the WGS84 ellipsoid. quaternion is the ARCore East-Up-South quaternion in [x, y, z, w] order.

createLocalCoordinateTransformer(origin)

Creates a reusable transformer whose origin and horizontal heading are defined by a Geospatial pose.

const transformer = createLocalCoordinateTransformer(pose);
console.log(transformer.headingRadians);

transformer.toLocalPosition(position)

Converts a WGS84 position in GeoJSON order into local coordinates in metres.

const localPosition = transformer.toLocalPosition([longitude, latitude, ellipsoidalAltitude]);

The result is [x, y, z]: positive X points right, positive Y points up, and negative Z points forward relative to the origin pose.

headingRadiansFromEastUpSouthQuaternion(quaternion)

Optionally extracts the signed horizontal heading in radians without creating a coordinate transformer.

import { headingRadiansFromEastUpSouthQuaternion } from "webxr-geospatial";

const heading = headingRadiansFromEastUpSouthQuaternion(pose.quaternion);

createYawOnlyViewerAlignment(transform)

Derives the position and horizontal rotation for placing the local geospatial scene at an initial WebXR viewer pose. Pitch and roll are discarded so the scene remains vertical. The result is independent of Three.js or any other rendering framework.

import { createYawOnlyViewerAlignment } from "webxr-geospatial";

const alignment = createYawOnlyViewerAlignment({
  position: [viewerPosition.x, viewerPosition.y, viewerPosition.z],
  quaternion: [viewerQuaternion.x, viewerQuaternion.y, viewerQuaternion.z, viewerQuaternion.w],
});

sceneRoot.position.set(...alignment.position);
sceneRoot.rotation.set(0, alignment.yawRadians, 0);

Platform notes

  • The Android localization flow requires a compatible companion app and an Android browser that can launch Android intents.
  • Production return URLs require HTTPS; loopback HTTP is supported for development.
  • Coordinate conversion does not require browser globals and can be used separately from the Android flow.
  • Node.js 20 or newer is required when the package is consumed in Node-based tooling.

Privacy

The companion app returns the pose in the URL fragment, which is not included in the HTTP request to the web server. The package removes this fragment before delivering a valid pose to application code. Applications should still treat location data as sensitive and avoid unnecessary logging or persistence.


Deutsch

webxr-geospatial verbindet eine Browseranwendung mit einer kompatiblen Android-Companion-App und rechnet WGS84-Positionen in horizontal ausgerichtete lokale Koordinaten für eine WebXR-Szene um.

Das Paket:

  • startet die Companion-App und übergibt ihr eine Rücksprungadresse;
  • liest und prüft die zurückgegebene Geospatial Pose;
  • entfernt die Lokalisierungsdaten aus der sichtbaren URL;
  • rechnet WGS84-Koordinaten über ECEF und ENU in lokale metrische Koordinaten um;
  • berücksichtigt den Yaw-Winkel der ARCore-East-Up-South-Quaternion, ohne die vertikale Achse zu neigen;
  • bestimmt aus einer initialen WebXR-Viewer-Pose eine frameworkneutrale horizontale Szenenausrichtung.

Es ist unabhängig von einem bestimmten Webframework, wird als ES-Modul mit TypeScript-Typdefinitionen ausgeliefert und besitzt keine Laufzeitabhängigkeiten.

Companion-App

Der Lokalisierungsablauf ist für die Verwendung mit der Android-App WebXR Geospatial Companion vorgesehen. Die App bestimmt die ARCore Geospatial Pose und gibt sie an den Browser zurück. Sie ist nicht Bestandteil dieses npm-Pakets und wird hier nicht verlinkt, da derzeit noch kein öffentliches Repository für die App verfügbar ist.

Die Koordinatenumrechnung kann unabhängig davon verwendet werden, wenn eine kompatible GeospatialPose aus einer anderen Quelle vorliegt.

Installation

npm install webxr-geospatial

Schnellstart

Der Pose-Handler sollte beim Laden der Webseite registriert werden. Die Lokalisierung muss durch eine Nutzereingabe gestartet werden, damit der Browser die Android-App öffnen darf.

import { createLocalCoordinateTransformer, handleGeospatialPose, startGeospatialLocalization } from "webxr-geospatial";

handleGeospatialPose((pose) => {
  const transformer = createLocalCoordinateTransformer(pose);

  // GeoJSON-Reihenfolge: Längengrad, Breitengrad, ellipsoidische Höhe
  const [x, y, z] = transformer.toLocalPosition([13.52584808, 52.45678137, 74.189]);

  console.log("Lokale Position:", { x, y, z });
});

document.querySelector("#locate")?.addEventListener("click", () => {
  startGeospatialLocalization();
});

Die ausgegebenen Koordinaten beziehen sich auf die Geospatial Pose. Die WebXR-Anwendung muss dieses lokale Koordinatensystem anschließend mit ihrem tatsächlichen WebXR-Referenzraum verbinden, beispielsweise anhand der ersten WebXR-Viewer-Pose.

Öffentliche Schnittstelle

startGeospatialLocalization(options?)

Öffnet die Android-Companion-App und übergibt die Adresse, zu der sie anschließend zurückkehren soll. Die Methode muss unmittelbar durch eine Nutzereingabe aufgerufen werden.

startGeospatialLocalization();

Alle Einstellungen sind optional:

startGeospatialLocalization({
  returnUrl: window.location.href,
  companionAppLink: "https://example.com/android/locate/",
  companionPackageId: "com.example.companion",
  installFallbackUrl: "https://example.com/android/install/",
});

| Option | Aufgabe | | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | returnUrl | Nach der Lokalisierung erneut geöffnete Browseradresse. Standardmäßig wird die aktuelle Adresse verwendet. | | companionAppLink | HTTPS-App-Link, der von der Companion-App verarbeitet wird. | | companionPackageId | Android-Paketkennung zur gezielten Adressierung der App. | | installFallbackUrl | Seite, die geöffnet wird, wenn die Companion-App nicht gestartet werden kann. Standardmäßig wird /android/install/ auf der aktuellen Origin verwendet. |

Rücksprungadressen müssen HTTPS verwenden. HTTP wird während der lokalen Entwicklung ausschließlich für Loopback-Adressen akzeptiert.

handleGeospatialPose(callback)

Liest synchron eine zurückgegebene Pose aus dem URL-Fragment, entfernt das Lokalisierungsfragment, prüft die enthaltenen Daten und ruft bei einer gültigen Pose den Callback auf.

handleGeospatialPose((pose) => {
  console.log(pose.latitude, pose.longitude, pose.altitude);
});

Der Callback erhält:

interface GeospatialPose {
  latitude: number;
  longitude: number;
  altitude: number;
  quaternion: readonly [number, number, number, number];
}

altitude bezeichnet die Höhe in Metern über dem WGS84-Ellipsoid. quaternion ist die ARCore-East-Up-South-Quaternion in der Reihenfolge [x, y, z, w].

createLocalCoordinateTransformer(origin)

Erzeugt einen wiederverwendbaren Transformator, dessen Ursprung und horizontale Ausrichtung durch eine Geospatial Pose festgelegt werden.

const transformer = createLocalCoordinateTransformer(pose);
console.log(transformer.headingRadians);

transformer.toLocalPosition(position)

Rechnet eine WGS84-Position in GeoJSON-Reihenfolge in lokale Koordinaten in Metern um.

const localPosition = transformer.toLocalPosition([longitude, latitude, ellipsoidalAltitude]);

Das Ergebnis besitzt die Form [x, y, z]: Die positive X-Achse zeigt nach rechts, die positive Y-Achse nach oben und die negative Z-Achse nach vorne relativ zur Ursprungspose.

headingRadiansFromEastUpSouthQuaternion(quaternion)

Bestimmt optional die horizontale Ausrichtung in Radiant, ohne dafür einen Koordinatentransformator zu erzeugen.

import { headingRadiansFromEastUpSouthQuaternion } from "webxr-geospatial";

const heading = headingRadiansFromEastUpSouthQuaternion(pose.quaternion);

createYawOnlyViewerAlignment(transform)

Bestimmt Position und horizontale Drehung, mit denen die lokale Geospatial-Szene an einer initialen WebXR-Viewer-Pose platziert wird. Pitch und Roll werden verworfen, damit die Szene vertikal bleibt. Das Ergebnis ist unabhängig von Three.js oder einem anderen Rendering-Framework.

import { createYawOnlyViewerAlignment } from "webxr-geospatial";

const alignment = createYawOnlyViewerAlignment({
  position: [viewerPosition.x, viewerPosition.y, viewerPosition.z],
  quaternion: [viewerQuaternion.x, viewerQuaternion.y, viewerQuaternion.z, viewerQuaternion.w],
});

sceneRoot.position.set(...alignment.position);
sceneRoot.rotation.set(0, alignment.yawRadians, 0);

Plattformhinweise

  • Der Android-Lokalisierungsablauf benötigt eine kompatible Companion-App und einen Android-Browser, der Android-Intents öffnen kann.
  • Produktive Rücksprungadressen benötigen HTTPS; Loopback-HTTP wird für die lokale Entwicklung unterstützt.
  • Die Koordinatenumrechnung benötigt keine Browser-APIs und kann unabhängig vom Android-Ablauf verwendet werden.
  • Bei einer Verwendung in Node-basierten Werkzeugen wird Node.js 20 oder neuer benötigt.

Datenschutz

Die Companion-App gibt die Pose über das URL-Fragment zurück, das nicht Bestandteil der HTTP-Anfrage an den Webserver ist. Das Paket entfernt dieses Fragment, bevor eine gültige Pose an den Anwendungscode übergeben wird. Standortdaten sollten dennoch als sensible Daten behandelt und nicht unnötig protokolliert oder gespeichert werden.

License / Lizenz

MIT