Device motion control and camera rotation calculation error

I’m trying to use a smartphone’s gyroscope to rotate a camera. It works great in portrait mode. However, in landscape mode it seems to be off by about 30 degrees. I have modified the code so I can get it to work properly on my iPhone, but trying to us the proper calculations for the rotation without modifications so that it will work with other devices as well.

        if (window.screen.orientation) {
            screenOrientation = window.screen.orientation.angle;
        } else if (typeof window.orientation === "number") {
            screenOrientation = window.orientation;
        } else if (window.screen.mozOrientationn) {
            screenOrientation = {
                "portrait-primary": 0,
                "portrait-secondary": 180,
                "landscape-primary": 90,
                "landscape-secondary": 270,

        orientation_a = event.alpha;
        orientation_b = event.beta;
        orientation_g = event.gamma;

        var rotType = (screenOrientation === 0 || screenOrientation === 180) ? "YXZ" : "YZX";

        if(rotType == "YZX") 
            if(orientation_g >= 0) {
                orientation_b = orientation_b - 30;
            } else {
                orientation_b = orientation_b + 30;

      var d2r = Math.PI / 180;

        const zee = new THREE.Vector3( 0, 0, 1 );
        const euler = new THREE.Euler();
        const q0 = new THREE.Quaternion();
        const q1 = new THREE.Quaternion( - Math.sqrt( 0.5 ), 0, 0, Math.sqrt( 0.5 ) ); // - PI/2 around the x-axis

        euler.set( orientation_b * d2r, orientation_a * d2r, - orientation_g * d2r, 'YXZ' ); // 'ZXY' for the device, but 'YXZ' for us
        camera.quaternion.setFromEuler( euler ); // orient the device
        camera.quaternion.multiply( q1 ); // camera looks out the back of the device, not the top
        camera.quaternion.multiply( q0.setFromAxisAngle( zee, - screenOrientation ) ); // adjust for screen orientation

I came across you issue as I was attempting something similar, but it turned out I had not payed enough attention in Math class, so I kind of gave up.

Hence I went with calling THREEx.DeviceOrientationControls(camera); within a webkit env. This is a less elegant option. However there I also experience this 30 deg offset (without introducing any of my own ‘calculations’).

So THREEx.DeviceOrientationControls(camera);, calls/uses the javascript window.addEventListener("orientationchange"..); Then the alpha, beta, gamma are used calculated by Webkit.

I do not want to put you off track but at this moment I kind of suspect that the problem might live inside three-js. I assume you are using geo coordinates as well to place Objects in a scene (e.g threex.add(new THREE.Mesh(geometry, material4), longitude, latitude); )? I’ll try to further exclude some other things as well and if I find something I’ll share it but for now just a message that you are not alone :slight_smile:

Also compasses seem to do whatever they like anyway (not set to geographical north). Especially when laying flat on a desk with some steel frame underneath and with or without other electronics in proximity. Not that this is the exact issue but it is an extra variable to take into account when testing.

So about my particular issue I proposed a PR within the AR-js-org repo. It turns out it is a known bug/choice (within WebKit) that the alpha is set to 0 whatever direction you happen to be facing at the time of initialising.

“Because that has been the case since iOS 4” - Apple

When DeviceOrientationControls was still present in Three-JS (and did something similar as you/me are attempting), there were multiple iOS issues (#10552, #4577,…) regarding this iOS feature/choice.

Later these patches were removed, and then DeviceOrientationControls was removed all together, with the reason that there were too many compatibly issues. Looking trough the history there also was a deviation with Android 7, but I believe there are no more besides the to be patched iOS issue.

Anyway within AR-js-org/AR.js DeviceOrientationControls still exists. I do not know your exact situation but maybe my conclusions and PR is of some use to you.

The relevant part is

        // iOS compass-calibrated 'alpha' patch
        // see:
        const heading = device.webkitCompassHeading || device.compassHeading;
        const alpha = device.alpha || heading
          ? MathUtils.degToRad(
              ? 360 - heading
              : device.alpha || 0) + scope.alphaOffset
          : 0; // Z