Understanding How the Accelerometer and Gyroscope Work in the Browser

Final Product (must have iOS 4.0 or greater to view): http://joelongstreet.com/blog_files/ios_accelerometer_1/index.html

I’ll be covering the basics of accessing the accelerometer and gyroscope with JavaScript in the post below. We’ll discuss difference between the two technologies and write a simple example to log out information when the device has detected a change in orientation. To simplify DOM manipulation, the code will depend on either Zepto or jQuery (I highly recommend Zepto when working in mobile).

In iOS 4.0 and above, Apple has allowed the web browser (via JavaScript) to access data returned from both the accelerometer and gyroscope. Supported devices include (only if the operating system is 4.0 or greater):

  • Accelerometer – iPhone 4, iPad, iPad2, iPod Touch
  • Gyroscope – iPhone 4, iPad2, iPod Touch

Unfortunately, Android devices do not currently support the use of the accelerometer or gyroscope in the web browser. You’ll have to stick to the rudimentary ‘onorientationchange’ event. If you’d like to read more about this event, the Safari Developer Library has several good explanations.

What’s the Difference between the Accelerometer and the Gyroscope?

Accelerometer vs. Gyroscope Visually Explained

Accelerometer (x, y, z) and Gyroscope (a, b, g)

The accelerometer measures orientation in the x, y, and z dimensions. If we rotate the device left or right (think of steering a car), the x and y values are changing. If we let the top of the device fall towards or away from us, the z dimension is changing.

The gyroscope measures forces relative to the original position of the device. Unlike the accelerometer, all of the channels of the gyroscope will start at 0 upon initialization and will return values based on the changed orientation. Moving the device vertically through space (think of doing squats) changes alpha. Twisting the device (ringint out a rag) changes the beta. Bringing the device towards or away from you (think of face punching), effects gamma.

By combining the 6 forces of the gyroscope and accelerometer we can summon Captain Planet and find the exact orientation of the device, but only relative to it’s starting position.

The Code

I’m starting with an entirely blank html file. The only things we’ll need to include are a reference to Zepto and a series of DOM elements to hold the textual information we’ll be logging out. You can download the final zip package here – http://joelongstreet.com/blog_files/ios_accelerometer_1/package.zip.

All I’m doing here is associating DOM elements with JavaScript variables. Nothing special.

1
2
3
4
5
6
var x_dom = $('.x');
var y_dom = $('.y');
var z_dom = $('.z');
var a_dom = $('.a');
var b_dom = $('.b');
var g_dom = $('.g');

Accelerometer – The “ondevicemotion” event is native to browsers that support the accelerometer. We can use this event to constantly track the changes in motion related to the x, y, and z dimensions. After collecting the data from the event, we write the information to the DOM.

1
2
3
4
5
6
7
8
9
window.ondevicemotion = function(event) {
   var x = event.accelerationIncludingGravity.x;
   var y = event.accelerationIncludingGravity.y;
   var z = event.accelerationIncludingGravity.z;
   
   x_dom.text(x);
   y_dom.text(y);
   z_dom.text(z);
}
Caution!

In the example above, the use of the word acceleration is misleading. Acceleration by definition is the rate of change of speed. This may lead you to believe that the returned values are dependent on how fast you’re moving the device, this is not the case. The values are constant and are entirely dependent on the devices position, not the acceleration required to achieve that position.

Gyroscope – We’re doing the same thing here with the gyroscope. Like the accelerometer, this method is native to devices that support the respective instrument.

1
2
3
4
5
6
7
8
9
window.ondeviceorientation = function(event) {
   var a = event.alpha;
   var b = event.beta;
   var g = event.gamma;
   
   a_dom.text(a);
   b_dom.text(b);
   g_dom.text(g);
}

If you log the above code, you’ll notice the instruments are very, very sensitive and the strings returned are somewhat long. Since it’s kind of difficult to look at that kind of information in the DOM, I’ve added a rounding function to display a single whole number. Also adding a + sign to the string makes switches between positive and negative numbers less visually striking.

The Final Product:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
var x_dom = $('.x');
var y_dom = $('.y');
var z_dom = $('.z');
var a_dom = $('.a');
var b_dom = $('.b');
var g_dom = $('.g');

//This is only for devices that support the use of a gyroscope (iPhone 4, iPad2, iPod Touch)
window.ondeviceorientation = function(event) {
   var a = Math.round(event.alpha*1/1);
   var b = Math.round(event.beta*1/1);
   var g = Math.round(event.gamma*1/1);
   if(a >= 0) { a = '+' + a}
   if(b >= 0) { b = '+' + b}
   if(g >= 0) { g = '+' + g}
   
   a_dom.text(a);
   b_dom.text(b);
   g_dom.text(g);
}

window.ondevicemotion = function(event) {
   var x = Math.round(event.accelerationIncludingGravity.x*1/1);
   var y = Math.round(event.accelerationIncludingGravity.y*1/1);
   var z = Math.round(event.accelerationIncludingGravity.z*1/1);
   
   if(x >= 0) { x = '+' + x}
   if(y >= 0) { y = '+' + y}
   if(z >= 0) { z = '+' + z}
   
   x_dom.text(x);
   y_dom.text(y);
   z_dom.text(z);
}

Finished, not a whole lot to it and pretty easy overall. I’m really looking forward to seeing how people use these new technologies to build web applications. Note: In real life scenarios it’s probably overkill to collect information on device motion and orientation change.

Downloads

Further Reading:

Webkit + JavaScript Particle Effect

Final Product: http://joelongstreet.com/blog_files/basic_particles

This post will demonstrate how to create a basic particle effect using CSS webkit animations and javascript. We’ll be using Zepto.js as our JavaScript framework to select and create DOM elements. If you’ve never used the library before, don’t worry. It’s syntax is identical to jQuery: the only difference is a smaller footprint and a far less robust API. If you’d like, you can also use the jQuery javascript framework.

You can see the final product here: http://joelongstreet.com/blog_files/basic_particles. You can download a zip package from here: http://joelongstreet.com/blog_files/basic_particles/package.zip.

No markup is provided because all of the HTML in this tutorial is generated via JavaScript. The CSS has one major class which sets a few properties – border-radius (make the element appear as a circle), position (gives us the ability to freely move the element around the stage), and a webkit transition property.

In reference to the webkit transition property, “All” refers to which attribute to manipulate (ex. top, left, width, height, etc.), .75 is the time the transition should take in seconds, and the third parameter is an easing function. You can read more about CSS transition properties at webkit.org. The rest of the classes specify colors for each particle.

1
2
3
4
5
6
.particle            {z-index:99; position:absolute; width:15px; height:15px; -webkit-border-radius:15px; -webkit-transition:all .75s ease-out;}
   .particle.purple  {background-color:rgba(108, 000, 072, 1);}
   .particle.green   {background-color:rgba(189, 190, 000, 1);}
   .particle.yellow  {background-color:rgba(246, 197, 000, 1);}
   .particle.orange  {background-color:rgba(221, 137, 000, 1);}
   .particle.red     {background-color:rgba(189, 058, 000, 1);}

I’ll be using random numbers frequently throughout this tutorial so let’s take advantage of a handy little function. If you pass two numbers into the function below, it will return a number within the range of the two numbers you specified. Neat, handy.

1
2
3
function random_from_to(from, to){
   return Math.floor(Math.random() * (to - from + 1) + from);
}

When generating particles, we know that they need to actually come from somewhere (some x and y location). Let’s start by capturing either a touch or click event and passing the corresponding x and y coordinates into a new particle.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//Mouse Events
$('body')[0].addEventListener('click', function(e){
   e.preventDefault();
   var x = e.pageX;
   var y = e.pageY;
   var particle = new Particles(x, y);
});

//or Touch Events if you like
$('body')[0].addEventListener('touchstart', function(e){
   e.preventDefault();
   var x = e.targetTouches[0].pageX;
   var y = e.targetTouches[0].pageY;
   var particle = new Particles(x, y);
});

The particle class doesn’t exist yet, so we’ll need to create it. We know we want the effect to use multiple colors, so let’s add that into the particle definition.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function Particles(x, y){
   
   //this will generate a random color for us whenever it's called
   //you could use as many colors as you like
   this.gen_color = function(){
      var rand = random_from_to(0, 4);
      switch(rand){
         case 0:
            rand = 'purple';
            break;
         case 1:
            rand = 'green';
            break;
         case 2:
            rand = 'yellow';
            break;
         case 3:
            rand = 'orange';
            break;
         case 4:
            rand = 'red';
            break;
      }
      return rand;
   };
   
   //Our HTML template will look something like this when rendered- <div class="particle green" style="top:241px; left:239px;"></div>
   //The x and y vlues come from our mouse/touch event and the generated color comes from the method above.
   var html_template = '<div class="particle ' + this.gen_color() + '" style="top:' + y + 'px; left:' + x + 'px;"></div>';
   
   //Put it on screen
   $('body').append(html_template);
   
};

We want more than one particle. We can specify a number, or generate a random one. Let’s do random for a more realistic effect. After we know how many particles we need to create, we then need to append each one to the stage. Move your html template inside of a for loop. Remember, the more particles we generate the slower our document will render.

1
2
3
4
5
6
var num_particles = random_from_to(1, 5);

for(var i=0; i<num_particles; i++){
   var html_template = '<div class="particle ' + this.gen_color() + '" style="top:' + y + 'px; left:' + x + 'px;"></div>';
   $('body').append(html_template);
}

It works, but it’s difficult too tell where each particle is since they’re all overlapping. To animate the particle, we can iterate over each particle element on the stage and apply a new random x and y position. If we immediately try to apply the new CSS properties to the particle element, we won’t see any animation. This is because the DOM has not yet recognized the css properties of our newly added element. We can force it to update with a setTimeout, 0 milliseconds required.

1
2
3
4
5
6
7
8
$('.particle').each(function(){
   var new_x = random_from_to(x - 100, x + 100);
   var new_y = random_from_to(y - 100, y + 100);
   setTimeout(function(){
      //but $(this) won't work...
      $(this).css({ 'left':new_x, 'top':new_y, 'opacity':0 });
   });
});

The placement of the setTimeout function interferes with our reference to the word $(this). In this instance, $(this) is actually referring to some fictional element within the timeout, we want it to refer to the DOM element. So let’s add a reference. I like to use the word self.

1
2
3
4
5
6
7
8
$('.particle').each(function(){
   var self = $(this);
   var new_x = random_from_to(x - 500, x + 500);
   var new_y = random_from_to(y - 500, y + 500);
   setTimeout(function(){
      self.css({ 'left':new_x, 'top':new_y, 'opacity':0 });
   });
});

If we’ve done everything correctly up to this point the basic effect should be present, but there is a problem. If you look at the DOM inspector in your browser you’ll notice that the particle elements can really start to build up. If you play with or document for a bit, you can build up hundreds or thousands of unused particles. We’ll need to remove those unused elements from the stage.

The easiest way is to set a timeout to remove everything with a class of particle.

1
2
3
setTimeout(function(){
   $('.particle').remove();
}, 500);

This is fine, but it can take away from the experience when we want to generate several sets of particles before removing other particles. To accomplish this, we need some kind of unique identifier for each set of particles we generate. We could use a random number, or even better, a javascript date object. Since the date object will return a number in milliseconds, it’s plenty accurate for our purposes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var uid = new Date().getTime();
for(var i=0; i<num_particles; i++){
   //the addition of the uid class here is so we can remove just this set
   var html_template = "<div class='particle uid_" + uid + ' ' + this.gen_color() + "' style='top:" + y + "px; left:" + x + "px;'></div>";
   $('body').append(html_template);
}

var particle_uid = '.uid_' + uid;
$(particle_uid).each(function(){
   var self = $(this);
   var new_x = random_from_to(x - 500, x + 500);
   var new_y = random_from_to(y - 500, y + 500);
   setTimeout(function(){
      self.css({ 'left':new_x, 'top':new_y, 'opacity':0 });
   });
   setTimeout(function(){
      $(particle_uid).remove();
      //make note of the 500, it should be set to the same value as your transition property in the CSS statement
   }, 500);
});

That’s pretty much it! I’ve posted the final Particle class code below (a little cleaned up). Here’s the final working model – http://joelongstreet.com/blog_files/basic_particles. You could use the particle class in a variety of ways so I’ve prepared another scenario in which particles are generated automatically – http://joelongstreet.com/blog_files/basic_particles/random_position.html.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
function Particles(x, y){
   
   var x_range = 500;
   var y_range = 500;
   var max_particles = 1;
   var min_particles = 5;
   
   this.gen_color = function(){
      var rand = random_from_to(0, 4);
      switch(rand){
         case 0:
            rand = 'purple';
            break;
         case 1:
            rand = 'green';
            break;
         case 2:
            rand = 'yellow';
            break;
         case 3:
            rand = 'orange';
            break;
         case 4:
            rand = 'red';
            break;
      }
      return rand;
   };

   var num_particles = random_from_to(min_particles, max_particles);
   var uid = new Date().getTime();
   for(var i=0; i<num_particles; i++){
      var html_template = "<div class='particle uid_" + uid + ' ' + this.gen_color() + "' style='top:" + y + "px; left:" + x + "px;'></div>";
      $('body').append(html_template);
   }
   
   var particle_uid = '.uid_' + uid;
   
   $(particle_uid).each(function(){
      var self = $(this);
      var new_x = random_from_to(x - x_range, x + x_range);
      var new_y = random_from_to(y - y_range, y + y_range);
      setTimeout(function(){
         self.css({ 'left':new_x, 'top':new_y, 'opacity':0 });
      });
      setTimeout(function(){
         self.remove();
      }, 500);
   });
};

Further Reading: