DEV Community

Cover image for Real Compass on mobile browsers with Javascript

Real Compass on mobile browsers with Javascript

orkhanjafarovr profile image Orkhan Jafarov ・3 min read

Well, one day I had a challenge from my muslim friend to code a map that's gonna show an arrow from his current geolocation to Qibla or any geopoint.

That wasn't the best solution, cause a compass will solve it better way and make people's life easier. So, I've started to find any package/lib to put the compass into his webpage.

Found these solutions Compass.js or this one, but none of them work at all well. Cause last commits were 6-7 years ago.

Let's make own real compass for mobile browsers!

Result we're gonna have Alt Text

We will need several html elements.

<div class="compass">
  <div class="arrow"></div>
  <div class="compass-circle"></div>
  <div class="my-point"></div>
<button class="start-btn">Start compass</button>
Enter fullscreen mode Exit fullscreen mode

Let's add css for that

.compass {
  position: relative;
  width: 320px;
  height: 320px;
  border-radius: 50%;
  box-shadow: 0 0 15px rgba(0, 0, 0, 0.2);
  margin: auto;

.compass > .arrow {
  position: absolute;
  width: 0;
  height: 0;
  top: -20px;
  left: 50%;
  transform: translateX(-50%);
  border-style: solid;
  border-width: 30px 20px 0 20px;
  border-color: red transparent transparent transparent;
  z-index: 1;

.compass > .compass-circle,
.compass > .my-point {
  position: absolute;
  width: 80%;
  height: 80%;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  transition: transform 0.1s ease-out;
  background: url( center
  background-size: contain;

.compass > .my-point {
  opacity: 0;
  width: 20%;
  height: 20%;
  background: rgb(8, 223, 69);
  border-radius: 50%;
  transition: opacity 0.5s ease-out;
Enter fullscreen mode Exit fullscreen mode

JavaScript time!

Define our html elements first and add event for button that starts it.
iOS needs to have manipulation by user to start DeviceOrientationEvent, but for Android it works without it.

const compassCircle = document.querySelector(".compass-circle");
const startBtn = document.querySelector(".start-btn");
const myPoint = document.querySelector(".my-point");
let compass;
const isIOS = !(
  navigator.userAgent.match(/(iPod|iPhone|iPad)/) &&
Enter fullscreen mode Exit fullscreen mode
function init() {
  startBtn.addEventListener("click", startCompass);

function startCompass() {
  if (isIOS) {
      .then((response) => {
        if (response === "granted") {
          window.addEventListener("deviceorientation", handler, true);
        } else {
          alert("has to be allowed!");
      .catch(() => alert("not supported"));
  } else {
    window.addEventListener("deviceorientationabsolute", handler, true);

function handler(e) {
  compass = e.webkitCompassHeading || Math.abs(e.alpha - 360); = `translate(-50%, -50%) rotate(${-compass}deg)`;

Enter fullscreen mode Exit fullscreen mode

Done! Our compass is working for both iOS and Android.

Upgrade our compass to reach the goal

On this step we need to find correct angle/degree to our point (Qibla).
We put the point coordinates and calculate degree from out current geolocation.

How it works?

  1. We're getting our current geolocation
  2. Define point coordinates (where we should turn to)
  3. Calculate degree from our position to defined point
  4. Display point when we're in correct position

Define pointDegree and our functions for this.

let pointDegree;

function locationHandler(position) {
  const { latitude, longitude } = position.coords;
  pointDegree = calcDegreeToPoint(latitude, longitude);

  if (pointDegree < 0) {
    pointDegree = pointDegree + 360;

function calcDegreeToPoint(latitude, longitude) {
  // Qibla geolocation
  const point = {
    lat: 21.422487,
    lng: 39.826206,

  const phiK = ( * Math.PI) / 180.0;
  const lambdaK = (point.lng * Math.PI) / 180.0;
  const phi = (latitude * Math.PI) / 180.0;
  const lambda = (longitude * Math.PI) / 180.0;
  const psi =
    (180.0 / Math.PI) *
      Math.sin(lambdaK - lambda),
      Math.cos(phi) * Math.tan(phiK) -
        Math.sin(phi) * Math.cos(lambdaK - lambda)
  return Math.round(psi);
Enter fullscreen mode Exit fullscreen mode

We put our location handler into init function to listen Geolocation API. Add some code to handler that's gonna update our point state.

function init() {
  startBtn.addEventListener("click", startCompass);

function handler(e) {
  compass = e.webkitCompassHeading || Math.abs(e.alpha - 360); = `translate(-50%, -50%) rotate(${-compass}deg)`;

  // ±15 degree
  if (
    (pointDegree < Math.abs(compass) && pointDegree + 15 > Math.abs(compass)) ||
    pointDegree > Math.abs(compass + 15) ||
    pointDegree < Math.abs(compass)
  ) { = 0;
  } else if (pointDegree) { = 1;
Enter fullscreen mode Exit fullscreen mode

We're done! We have a real compass in our mobile browsers.

Demo link

Here's a source link

by @gigantz

Discussion (3)

Editor guide
emiltoteb profile image
Emil Totev

Hey, thank you for the great tutorial! I've got it working flawlessly on iOS, but there appears to be an issue on Android - North is always in the direction the phone is pointing when the page loads, and if you're not looking north, all the directions are miscalculated. Have you faced this issue and were you able to overcome it?

orkhanjafarovr profile image
Orkhan Jafarov Author

I hope your Android device was using GPS with high accuracy mode. If not it will not work as expected. Try to get or debug heading value from geolocation or compassHeading. I didn’t faced this issue on Android

shaikh profile image
Javed Shaikh

This is great. Thanks for sharing 👏👏