A photo of Max Glenister Max Glenister

20: Which way is North? Part 2

In yesterday’s post I talked about the problem of using Cardinal North with device orientation, and how Three.js’ Device Orientation Camera provides a utility function for setting the alphaOffsetAngle, today we’re going to cover building a device to use this function.

We’re going to create a device that let’s you change the orientation offset for the camera, so we can move the scene-relative North. This isn’t something that you’re likely to need to use a lot, so it doesn’t need to be accessible from within VR-mode.

You can see in the diagram above two “safe areas”, these are unlikely to be visible while using Cardboard, so either are a good place to put the device. I’m going to use the bottom safe area.

The device should show the position of our “North” as well as the current rotation. I created a couple of images that will help with this:

Now to make them actually rotate. I’ll create the following markup for the device and inject it with JavaScript:

<button class="calibrate-orientation">
  <span class="offset"></span>
  <span class="actual"></span>

And the following styles for the device:

.calibrate-orientation {
  outline: none;
  background: #fff;
  border-radius: 100%;
  border: 0;
  padding: 0;
  box-shadow: 0 0 80px 20px #000;
  box-sizing: border-box;
  position: absolute;
  left: 0; right: 0; bottom: 0;
  width: 50px; height: 50px;
  margin: auto auto 20px;
.calibrate-orientation span {
  display: block;
  width: 100%;
  height: 100%;
  position: absolute;
  left: 0; top: 0;
  background-size: contain;
.calibrate-orientation .offset {
  background-image: url(../js/compass_current.png);
.calibrate-orientation .actual {
  background-image: url(../js/compass_actual.png);

We actually create/inject all of the markup and styles using JavaScript, but what you see there is the end result.

We only want to show this device on hardware that supports device orientation, which we can mostly assume is any mobile device, so we have a return early on in our function so that they don’t bother running the function:

var createOrientationCalibrationControl = function (controls) {
  // If we're not on mobile, get out of here
  if (!core.isPocketDevice()) return;

  // ... Rest of function

We use requestAnimationFrame to update the CSS transform property of our images to show the rotation:

var elClass = '.calibrate-orientation';
window.updateCompassControl = function () {
  var alpha = controls.deviceOrientation.alpha;
  var alpha_offset = controls.alphaOffsetAngle || 0;
  var alpha_actual = T.Math.degToRad(-alpha);
  if (alpha !== undefined) {
    var el = document.querySelectorAll(elClass+' .offset')[0];
      'transform: rotate('+alpha_offset+'rad)'

    var el = document.querySelectorAll(elClass+' .actual')[0];
      'transform: rotate('+alpha_actual+'rad)'

To do the actual job of updating the alphaOffsetAngle, we set an event listener on the button we’ve created:

button.addEventListener('click', function (e) {
  var alpha = controls.deviceOrientation.alpha;
  var offset = T.Math.degToRad(-alpha);

As I’ve implemented this in my core.js (you can see the code here: core.js#L84) this will automatically be carried over to all of the Cardboctober hacks that I’ve built so far, and will be available to any future hacks.

I need to play with the device a bit more to make it work perfectly, but for now it’s pretty functional.


Check out my other Cardoctober posts here: /cardboctober

About the author

A photo of Max Glenister

Max Glenister is a Front-end Developer based in Oxfordshire. For work he spends his time designing, validating and implementing user interfaces. For fun he tinkers with Virtual Reality, 3D printing, embedded systems, game development and many other things.

You can keep up with Max on Github, Twitter and Reddit