Know Thy Gyroscope and JS – Part II

Know Thy Gyroscope and JS – Part II

This is the second part of a two-part-post. Part I was about how to access the gyroscope and accelerometer data on mobile devices via JavaScript. This part will focus on the differences between devices and browsers. If you are not comfortable with the basics I suggest you take a look at Part I.

To start with, here is a little video of two different devices (iPhone 5 and HTC One). You can see the first alpha value and the gravitation related values are initially different between devices. Using JavaScript you can make them return the same values in every device. This way a more uniform experience can be provided.

Please note that these JavaScript events are fairly new standards. So the implementation across devices and browsers are different. Therefore they provide different values. I can imagine in a couple of years all devices and mobile browsers will implement the standard as they should. But for now it is useful to know what to expect and how to deal with them.

window.ondeviceorientation

event.alpha

The event.beta and event.gamma values are the same for all devices. There is difference in the event.alpha value. This is the rotation of the device around z-axis. So when the device is lying on the ground with the screen up, this angel shows where the top of the device is pointing.

Some devices provide this value with respect to the actual north of the world. In this case you get and absolute value. Some provide this value with respect to the initial position of the device when the event is called for the first time. Meaning when ever you refresh the page the event.alpha becomes 0.

There is a 4th value of the event (event.absolute) telling you if the device is providing absolute or relative values. Normally you should be able check this value and adjust the alpha. However some older devices/browsers do not return this value correctly.

So what you can do is to store the first event.alpha value in a variable. Later when providing the actual value you can use this first value as an offset. So you make sure you always provide a value relative to the initial position.

var isFirstTime = true;
var alphaOffset = 0;
var alpha = 0;
		
window.ondeviceorientation = function(event){
    if(isFirstTime){
        alphaOffset = event.alpha;
        isFirstTime = false;
        return;
    }
			
    alpha = event.alpha - alphaOffset;
}

 

When you apply the offset value, sometimes the result becomes negative. This means a negative angel. So in this case you need to subtract the absolute value of it from 360. The code becomes like this.

var isFirstTime = true;
var alphaOffset = 0;
var alpha = 0;
		
window.ondeviceorientation = function(event){
    if(isFirsTime){
        alphaOffset = event.alpha;
        isFirstTime = false;
        return;
    }
			
    alpha = event.alpha - alphaOffset;
    alpha = (alpha < 0)?(360 - Math.abs(alpha)):alpha;
}

 

Compass Calibration

It might be that the compass of the device is not calibrated. In this case the event.alpha values will not change (or change a little) even if you turn the device all the way around z-axis. However if this happens the device should trigger another event called window.oncompassneedscalibration. You can find the details of that event in Part I of this post.

window.ondevicemotion

In some browsers this event is triggered as expected. However the returned object does not have any of the expected values. (event.acceleration, event.rotationrate etc.) So when you are using this event make sure the event exists on the window object and returns the values you expect.

If the values are missing there is not much you can do. (e.g. on Android 4.2.2 native browser) But if they exist, the main difference is in the value of  event.accelerationincludinggravity.

Conceptually, the gravitation vector is towards the world. So if the device is laying on the floor, screen facing up, the gravitation vector should be lying in negative z-axis. Meaning the event.accelerationincludinggravity.z should be negative. Some devices return negative values as expected, however some return the same value but positive.

There is no other value that can tell you if the device is returning positive or negative gravitation values. But you can assume that when the page is loaded for the first time, the user is probably holding the device up right and facing the device. Which means the z-axis component of the gravitation vector lies in the negative direction. So in the first load of the page you can check if the event.accelerationincludinggravity.z is negative or positive. If it is positive you provide the later values with the coefficient -1.

Please note that if the user is holding the device in a weird orientation (e.g. up side down) this workaround would not work.

var isFirstTime = true;
var gravityCoefficient = 1;
var accGravityX = 0;
var accGravityY = 0;
var accGravityZ = 0;
		
window.ondevicemotion = function(event){
    if(isFirsTime){
        gravityCoefficient = (event.accelerationincludinggravity.z < 0)?1:-1;
        isFirstTime = false;
        return;
    }
			
    accGravityX *= gravityCoefficient;
    accGravityY *= gravityCoefficient;
    accGravityZ *= gravityCoefficient;
}

I am planning to write a small JavaScript file that handles these issues but this will be in a later post.

UPDATE: I have written the file. You can find it here: https://github.com/dorukeker/gyronorm.js