I’ve begun a project trying to using magnetometers to detect a user’s behaviour, so to begin to understand how to use them I built a simple compass. Step 1 — Raw Magnetometer data: Using the app for iPhone, I recorded the 3-axis magnetometer readings at various compass headings and device orientations. With the device constrained to the horizontal plane, the following (very promising) reading are obtained: PowerSense X & Y magnetometer readings at different headings Besides from the x-axis being reversed, these points are very usable to give us a compass heading! A conversion to spherical co-ordinates is used to make the output more usable: From Maths Stack Exchange As the magnitude of the magnetic field vector in 3D space is constant with changing device orientation, r value can be ignored in this application. In the horizontal plane, the rho value is also constant due to constant z-axis readings, so is also ignored for now. Theta is calculated using: def direction(vec):#vec = [x,y,z] from imported data x = -vec\[0\] #negate x y = vec\[1\] z = vec\[2\] if y > 0 and x > 0: #if in Q1 theta = math.atan(x/y) elif y < 0: #if in Q2 or Q3 theta = math.atan(x/y) + math.pi else: #if in Q4 theta = math.atan(x/y) + math.pi\*2 return theta This gives us a heading which is consistent with the angles that the data was recorded at! Now all that is needed is to get this magnetometer data from a mobile device. Step 2 — Realise that iPhones don’t give raw magnetometer data to web apps… Probably should have checked this part first. Oh well. Step 3 — Discover the wonder of the deviceorientation event deviceorientation allows a website to gather data of how a device is being held, which is reported in 3 different axis: — Rotation around the z axis, from 0 to 360 degrees alpha — Rotation around the x axis, from -180 to 180 degrees beta — Rotation around the y axis, from -90 to 90 degrees gamma The caveat is that this information is specified in a subtly different way between iOS and Android devices. Before chrome 50, Android devices would report alpha as an ‘ ’ value, constant in the reference frame of the earth’s surface. This means that alpha = 0 corresponds to the device being pointed due north, which is really handy. As seen in the magnetometer data, the rotation direction is reversed. This can be solved by taking absolute heading = 360 - alpha function deviceOrientationListener(event) {var alpha = event.alpha; //z axis rotation [0,360)var beta = event.beta; //x axis rotation [-180, 180]var gamma = event.gamma; //y axis rotation [-90, 90]var heading = 360 - alpha; //heading [0, 360)} if(window.DeviceOrientationEvent){ //Check if device is compatiblewindow.addEventListener("deviceorientation", deviceOrientationListener);} However, now Android has gone the way of iOS, reporting alpha as a ‘ ’ value, where 0 is defined as the device’s direction when the page is loaded. This makes is substantially less useful as a compass… relative Luckily, hidden in the depths of google developer pages lies a solution! Some devices will still report a absolute alpha value in the form of a compass heading through event.webkitCompassHeading. The application checks if the device will report this value, and if so uses that. Otherwise, relative alpha values are used to show the page, just not pointing north. ...if (typeof event.webkitCompassHeading !== "undefined") {alpha = event.webkitCompassHeading; //iOS non-standardvar heading = alphadocument.getElementById("heading").innerHTML = heading.toFixed([0]);}else {alert("Your device is reporting relative alpha values, so this compass won't point north! ");var heading = 360 - alpha; //heading [0, 360)document.getElementById("heading").innerHTML = heading.toFixed([0]);}... if (window.DeviceOrientationAbsoluteEvent) {window.addEventListener("DeviceOrientationAbsoluteEvent", deviceOrientationListener);} // If not, check if the device sends any orientation dataelse if(window.DeviceOrientationEvent){window.addEventListener("deviceorientation", deviceOrientationListener);} // Send an alert if the device isn't compatibleelse {alert("Sorry, try again on a compatible mobile device!");} All of the code is given at the bottom of the page. The compass can be tested , but if your device doesn’t work, have a look below: here The compass in action <!DOCTYPE html><html><head><style>p {font-family: verdana;font-size: 400px;color: #FFFFFF;}</style> <title>Compass</title> <script> // Get event data function deviceOrientationListener(event) { var alpha = event.alpha; //z axis rotation \[0,360) var beta = event.beta; //x axis rotation \[-180, 180\] var gamma = event.gamma; //y axis rotation \[-90, 90\] //Check if absolute values have been sent if (typeof event.webkitCompassHeading !== "undefined") { alpha = event.webkitCompassHeading; //iOS non-standard var heading = alpha document.getElementById("heading").innerHTML = heading.toFixed(\[0\]); } else { alert("Your device is reporting relative alpha values, so this compass won't point north :("); var heading = 360 - alpha; //heading \[0, 360) document.getElementById("heading").innerHTML = heading.toFixed(\[0\]); } // Change backgroud colour based on heading // Green for North and South, black otherwise if (heading > 359 || heading < 1) { //Allow +- 1 degree document.body.style.backgroundColor = "green"; document.getElementById("heading").innerHTML = "N"; // North } else if (heading > 179 && heading < 181){ //Allow +- 1 degree document.body.style.backgroundColor = "green"; document.getElementById("heading").innerHTML = "S"; // South } else { // Otherwise, use near black document.body.style.backgroundColor = "#161616"; } } // Check if device can provide absolute orientation data if (window.DeviceOrientationAbsoluteEvent) { window.addEventListener("DeviceOrientationAbsoluteEvent", deviceOrientationListener); } // If not, check if the device sends any orientation data else if(window.DeviceOrientationEvent){ window.addEventListener("deviceorientation", deviceOrientationListener); } // Send an alert if the device isn't compatible else { alert("Sorry, try again on a compatible mobile device!"); } </script> </head> <body><br><br><p id="heading" style="text-align:center"></p></body></html>