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

@misque/qibla

v0.2.1

Published

Qibla direction calculator for any location

Readme

@misque/qibla

Calculate the Qibla direction (direction of the Kaaba in Mecca) from any location on Earth. Includes support for device orientation sensors for compass applications.

Installation

npm install @misque/qibla
# or
yarn add @misque/qibla
# or
pnpm add @misque/qibla

Features

  • Accurate Qibla direction calculation using the great circle formula
  • Distance to Kaaba calculation using the Haversine formula
  • Compass direction labels (N, NE, E, SE, S, SW, W, NW, and 16-point variants)
  • Device orientation support for mobile compass applications
  • Magnetic declination adjustment support
  • Alignment detection and turn direction guidance
  • Full TypeScript support
  • Zero external runtime dependencies
  • Tree-shakeable ESM and CommonJS builds
  • Works in Node.js, browsers, React, React Native, and Edge runtimes

Quick Start

import { calculateQibla, formatQiblaBearing } from '@misque/qibla';

// Calculate Qibla from New York City
const result = calculateQibla({
  latitude: 40.7128,
  longitude: -74.006,
});

if (result.success) {
  console.log(`Qibla bearing: ${result.data.bearing}°`);
  // "Qibla bearing: 58.48°"

  console.log(`Direction: ${result.data.compassDirection}`);
  // "Direction: ENE"

  console.log(`Distance to Kaaba: ${result.data.distance} km`);
  // "Distance to Kaaba: 9513.2 km"

  console.log(formatQiblaBearing(result.data.bearing));
  // "58° (ENE)"
}

Using the Compass Class

import { QiblaCompass } from '@misque/qibla';

// Create a compass for a location
const compass = new QiblaCompass({
  latitude: 51.5074,
  longitude: -0.1278,
});

// Get the Qibla bearing
console.log(`Qibla: ${compass.getBearingFormatted()}`);
// "Qibla: 118.9°"

console.log(`Direction: ${compass.getCardinalDirection()}`);
// "Direction: SE"

console.log(`Distance: ${compass.getDistance()} km`);
// "Distance: 4566.2 km"

API Reference

Core Functions

calculateQibla(location: Coordinates, options?: QiblaOptions): Result<QiblaDirection>

Calculate the Qibla direction from a given location.

import { calculateQibla } from '@misque/qibla';

const result = calculateQibla({
  latitude: 40.7128,
  longitude: -74.006,
});

if (result.success) {
  const { bearing, distance, compassDirection } = result.data;
  console.log(`Face ${bearing}° (${compassDirection}), ${distance} km to Mecca`);
}

With magnetic declination adjustment:

const result = calculateQibla(
  { latitude: 40.7128, longitude: -74.006 },
  { magneticDeclination: -13 } // New York's approximate declination
);

bearingToCompassDirection(bearing: number): CompassDirection

Convert a bearing angle to a 16-point compass direction.

import { bearingToCompassDirection } from '@misque/qibla';

bearingToCompassDirection(0); // 'N'
bearingToCompassDirection(45); // 'NE'
bearingToCompassDirection(90); // 'E'
bearingToCompassDirection(135); // 'SE'
bearingToCompassDirection(180); // 'S'
bearingToCompassDirection(225); // 'SW'
bearingToCompassDirection(270); // 'W'
bearingToCompassDirection(315); // 'NW'
bearingToCompassDirection(22.5); // 'NNE'
bearingToCompassDirection(67.5); // 'ENE'

getKaabaCoordinates(): Coordinates

Get the geographic coordinates of the Kaaba.

import { getKaabaCoordinates } from '@misque/qibla';

const kaaba = getKaabaCoordinates();
// { latitude: 21.4225, longitude: 39.8262 }

isAtKaaba(location: Coordinates, thresholdKm?: number): boolean

Check if a location is at or very near the Kaaba.

import { isAtKaaba } from '@misque/qibla';

isAtKaaba({ latitude: 21.4225, longitude: 39.8262 }); // true
isAtKaaba({ latitude: 21.4225, longitude: 39.8262 }, 0.01); // true (10m threshold)
isAtKaaba({ latitude: 21.43, longitude: 39.83 }); // true (within 100m default)
isAtKaaba({ latitude: 40.7128, longitude: -74.006 }); // false

formatQiblaBearing(bearing: number, includeDirection?: boolean): string

Format a bearing angle as a human-readable string.

import { formatQiblaBearing } from '@misque/qibla';

formatQiblaBearing(118.5); // "119° (ESE)"
formatQiblaBearing(118.5, false); // "119°"
formatQiblaBearing(45); // "45° (NE)"

QiblaCompass Class

A class for compass applications with device orientation support.

Constructor

import { QiblaCompass } from '@misque/qibla';

const compass = new QiblaCompass(
  { latitude: 40.7128, longitude: -74.006 },
  {
    useTrueNorth: false, // Use magnetic north by default
    alignmentThreshold: 5, // Degrees tolerance for alignment
    onOrientationChange: (viz) => {
      // Called when device orientation changes
      console.log(viz.turnDirection);
    },
  }
);

Methods

// Get the Qibla bearing in degrees (0-360)
compass.getBearing(): number

// Get the bearing as a formatted string
compass.getBearingFormatted(precision?: number): string

// Get the cardinal direction (N, NE, E, SE, S, SW, W, NW)
compass.getCardinalDirection(): string

// Get compass visualization data
compass.getVisualization(deviceHeading?: number | null): CompassVisualization

// Get distance to Kaaba in kilometers
compass.getDistance(): number

// Update the location
compass.updateCoordinates(coordinates: Coordinates): void

// Get current device orientation (if tracking)
compass.getCurrentOrientation(): DeviceOrientation | null

Device Orientation Methods (Browser Only)

// Check if device orientation is supported
QiblaCompass.isOrientationSupported(): boolean

// Request permission for device orientation (iOS 13+)
await QiblaCompass.requestOrientationPermission(): Promise<PermissionState>

// Start tracking device orientation
await compass.startOrientationTracking(options?: QiblaCompassOptions): Promise<void>

// Stop tracking
compass.stopOrientationTracking(): void

// Get helper text for UI
QiblaCompass.getHelperText(visualization: CompassVisualization): string

// Calculate angle difference between two bearings
QiblaCompass.calculateAngleDifference(bearing1: number, bearing2: number): number

Factory Function

import { createQiblaCompass } from '@misque/qibla';

const compass = createQiblaCompass(
  { latitude: 40.7128, longitude: -74.006 },
  { alignmentThreshold: 3 }
);

Types

Coordinates

interface Coordinates {
  latitude: number; // -90 to 90
  longitude: number; // -180 to 180
}

QiblaDirection

interface QiblaDirection {
  bearing: number; // 0-360 degrees from true north
  distance: number; // Kilometers to Kaaba
  compassDirection: CompassDirection;
}

CompassDirection

16-point compass directions.

type CompassDirection =
  | 'N'
  | 'NNE'
  | 'NE'
  | 'ENE'
  | 'E'
  | 'ESE'
  | 'SE'
  | 'SSE'
  | 'S'
  | 'SSW'
  | 'SW'
  | 'WSW'
  | 'W'
  | 'WNW'
  | 'NW'
  | 'NNW';

QiblaOptions

interface QiblaOptions {
  magneticDeclination?: number; // Degrees to adjust for magnetic north
}

CompassVisualization

interface CompassVisualization {
  qiblaBearing: number; // Qibla bearing from North (0-360)
  deviceHeading: number | null; // Current device heading
  angleDifference: number | null; // Difference between heading and Qibla
  turnDirection: 'left' | 'right' | 'aligned' | null;
  alignmentDistance: number | null; // Degrees to turn (0-180)
}

DeviceOrientation

interface DeviceOrientation {
  heading: number | null; // Degrees from magnetic north
  trueHeading: number | null; // Degrees from true north
  alpha: number | null; // Device rotation around z-axis
  beta: number | null; // Device rotation around x-axis
  gamma: number | null; // Device rotation around y-axis
}

QiblaCompassOptions

interface QiblaCompassOptions {
  useTrueNorth?: boolean; // Default: false
  onOrientationChange?: (visualization: CompassVisualization) => void;
  alignmentThreshold?: number; // Degrees tolerance (default: 5)
}

Examples

Simple Qibla Lookup

import { calculateQibla } from '@misque/qibla';

const cities = [
  { name: 'London', lat: 51.5074, lon: -0.1278 },
  { name: 'New York', lat: 40.7128, lon: -74.006 },
  { name: 'Tokyo', lat: 35.6762, lon: 139.6503 },
  { name: 'Sydney', lat: -33.8688, lon: 151.2093 },
];

cities.forEach(({ name, lat, lon }) => {
  const result = calculateQibla({ latitude: lat, longitude: lon });
  if (result.success) {
    console.log(
      `${name}: ${result.data.bearing.toFixed(1)}° (${result.data.compassDirection})`
    );
  }
});

// London: 118.99° (ESE)
// New York: 58.48° (ENE)
// Tokyo: 293.02° (WNW)
// Sydney: 277.50° (W)

Building a Mobile Compass App

import { QiblaCompass } from '@misque/qibla';

async function initQiblaCompass() {
  // Get user's location
  const position = await new Promise<GeolocationPosition>((resolve, reject) => {
    navigator.geolocation.getCurrentPosition(resolve, reject);
  });

  const compass = new QiblaCompass({
    latitude: position.coords.latitude,
    longitude: position.coords.longitude,
  });

  // Check if device orientation is supported
  if (!QiblaCompass.isOrientationSupported()) {
    // Fallback: just show the bearing
    console.log(`Qibla is ${compass.getBearingFormatted()} from North`);
    return;
  }

  // Start tracking orientation
  try {
    await compass.startOrientationTracking({
      alignmentThreshold: 3,
      onOrientationChange: (viz) => {
        const helperText = QiblaCompass.getHelperText(viz);
        updateUI(helperText, viz);
      },
    });
  } catch (error) {
    console.error('Failed to start orientation tracking:', error);
  }
}

function updateUI(text: string, viz: CompassVisualization) {
  document.getElementById('helper-text')!.textContent = text;
  document.getElementById('compass')!.style.transform = `rotate(${viz.qiblaBearing - (viz.deviceHeading ?? 0)}deg)`;

  if (viz.turnDirection === 'aligned') {
    document.body.classList.add('aligned');
    // Maybe vibrate to indicate alignment
    navigator.vibrate?.(200);
  } else {
    document.body.classList.remove('aligned');
  }
}

React Component Example

import { useState, useEffect } from 'react';
import { QiblaCompass, type CompassVisualization } from '@misque/qibla';

function QiblaCompassComponent({ latitude, longitude }: { latitude: number; longitude: number }) {
  const [compass] = useState(() => new QiblaCompass({ latitude, longitude }));
  const [visualization, setVisualization] = useState<CompassVisualization | null>(null);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    compass.updateCoordinates({ latitude, longitude });
  }, [compass, latitude, longitude]);

  useEffect(() => {
    if (!QiblaCompass.isOrientationSupported()) {
      setError('Device orientation not supported');
      return;
    }

    compass
      .startOrientationTracking({
        onOrientationChange: setVisualization,
      })
      .catch((err) => setError(err.message));

    return () => compass.stopOrientationTracking();
  }, [compass]);

  if (error) {
    return (
      <div>
        <p>{error}</p>
        <p>Qibla is {compass.getBearingFormatted()} from North</p>
      </div>
    );
  }

  if (!visualization) {
    return <p>Loading compass...</p>;
  }

  return (
    <div>
      <div
        style={{
          transform: `rotate(${visualization.qiblaBearing - (visualization.deviceHeading ?? 0)}deg)`,
        }}
      >
        {/* Compass arrow pointing to Qibla */}
        <span role="img" aria-label="arrow">
          ^
        </span>
      </div>
      <p>{QiblaCompass.getHelperText(visualization)}</p>
      <p>Distance: {compass.getDistance().toFixed(0)} km</p>
    </div>
  );
}

Using with Magnetic Declination

Magnetic declination (the difference between magnetic north and true north) varies by location. For precise compass readings, you may want to adjust for this:

import { calculateQibla } from '@misque/qibla';

// Get declination from a service like NOAA's World Magnetic Model
const declination = -13; // Example: New York is about -13°

const result = calculateQibla(
  { latitude: 40.7128, longitude: -74.006 },
  { magneticDeclination: declination }
);

if (result.success) {
  console.log(`Magnetic bearing to Qibla: ${result.data.bearing}°`);
}

TypeScript Support

This package is written in TypeScript and includes complete type definitions.

import type {
  QiblaDirection,
  QiblaOptions,
  CompassDirection,
  LocationWithQibla,
  DeviceOrientation,
  CompassVisualization,
  QiblaCompassOptions,
} from '@misque/qibla';

Related Packages

License

MIT