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.

Cardboctober day 20

Check out all of my other Cardoctober posts here: /cardboctober.


Want to comment? You can do so via Github.
Comments via Github are currently closed.


Want to reply? I've hooked up Webmentions so you can do so by sending a Webmention, or a Tweet mentioning this post.

Sent a Webmention but it's not showing up below? It could take a little while for brid.gy to pick it up. Webmentions are cached locally for 30 mins before attempting to fetch new Webmentions.