Track hand rotational angle – Leap + Javascript
In this tutorial, we are going to teach you how you can track your hand rotation using Leap Motion Controller. Leap Motion Controller is a device that allows you to detect your hand motions and gestures, and perform any desired action on them. It provides SDK in Java, Javascript, Python, C++, and Unreal Engine. We will be using Javascript in this tutorial.
You can buy the Leap Motion Controller from their official website. After buying, connect the device with your computer using a USB or USB-C port. Then download the SDK from here and do the installation.
If you prefer the video tutorial:
From the above video, you can see what we are going to create. So you will need a Doctor Strange time-spell video and some images. You will also need the Leap JS library to connect with Leap Motion Controller. You can find all 3 in the source files below.
Setup the Project
The following code helps you to reverse a video and concatenate 2 videos using FFmpeg:
// reverse a video
// ffmpeg -i Pentagramm2_green.mp4 -vf reverse reversed.mp4
// concatenate 2 videos
// ffmpeg -i Pentagramm2_green.mp4 -i reversed.mp4 -filter_complex "[0:v] [0:a] [1:v] concat=n=2:v=1:a=1 [v] [a]" -map "[v]" -map "[a]" output.mp4
Then create a video tag for the time-spell video.
<video src="output.mp4" muted id="time-spell"></video>
After that, create a div to show the image and text below it.
<div style="position: absolute; right: 50px; top: 50px;">
<img src="images/2021.JPG" id="image" />
<h1 id="title">2021</h1>
</div>
Now include the Leap JS library in your project.
<script src="leap-0.6.4.min.js"></script>
Then apply some CSS styles to display the video, image, and text properly.
<style>
/* remove margin and set background color as black */
body {
margin: 0px;
background-color: black;
}
/* set width of time spell and its position */
#time-spell {
width: 1200px;
position: absolute;
left: -275px;
top: 30px;
}
/* set width and height of image and fit inside container */
#image {
width: 800px;
height: 600px;
object-fit: contain;
}
/* set color, position, alignment, font size and background color of image text */
#title {
color: white;
position: relative;
bottom: 129px;
text-align: center;
font-size: 70px;
background-color: rgba(0, 0, 0, 0.7);
}
</style>
Then create an array of images and their text in Javascript.
<script>
// list of images and their text
var images = [{
"src": "images/2021.JPG",
"title": "2021"
}];
// index is out of bound right now
var index = images.length;
</script>
After that, initialize some variables that we will be using throughout the tutorial.
// total duration of time spell video
var totalDuration = 40;
// state of time spell (back or forward)
var state = "";
// previous state of time spell
var previousState = "";
// hand rotation from Leap Motion Controller
var handRotation = "";
// is time spell start rotating
var isMediaPlaying = false;
// video tag
var media = document.getElementById("time-spell");
Leap motion controller connection
Now is the time to connect our Javascript app with Leap Motion Controller.
// setup Leap loop with frame callback function
var controllerOptions = {};
Leap.loop(controllerOptions, function(frame) {
// get all hands in frame
var hands = frame.hands;
// if there is any hand
if (hands.length > 0) {
// get the first hand
var hand = hands[0];
// ignore if the hand is left
if (hand.isLeft) {
return false;
}
// get direction of hand (x,y,z co-ordinates)
var axis = hand.direction;
// get x-axis
axis = axis[0];
if (axis > 0 && axis < 0.9) {
// if hand is rotated to right
handRotation = "right";
} else if (axis > -0.09 && axis < -0.01) {
// if hand is in center (no rotation)
handRotation = "center";
media.pause();
} else if (axis < 0 && axis < -0.5) {
// if hand is rotated to left
handRotation = "left";
}
} else {
// if there is no hand in the frame, simply pause the time spell
if (media != null) {
media.pause();
}
}
});
For explanation, comments have been added with each line. Now we need to call a function every 0.5 seconds and rotate the time spell to the left if the hand is rotated to left, and right if the hand is rotated to right. Also, it will call a function to change the image as per hand rotation.
// called every 0.5 seconds
setInterval(function () {
// if hand is rotating to right
if (handRotation == "right") {
// only called once to rotate time spell
if (state == "") {
// rotate to right
mediaBackward();
}
// if time spell starts rotating
if (isMediaPlaying) {
// show next image
nextImage();
}
// make it empty to give a delay to show next image
handRotation = "";
} else if (handRotation == "left") {
// only called once to rotate time spell
if (state == "") {
// rotate to left
mediaForward();
}
// if time spell starts rotating
if (isMediaPlaying) {
// show previous image
previousImage();
}
// make it empty to give a delay to show previous image
handRotation = "";
} else if (handRotation == "center") {
// pause the time spell, when hand is in center of Leap Motion Controller
media.pause();
}
}, 500);
Now we just need to create these 4 functions called from the above code.
// when hand is rotated right
function mediaBackward() {
// "back" means reverse
state = "back";
// starts rotating time spell
media.play();
// current time of video
var currentTime = media.currentTime;
// if video stops playing
if (currentTime <= 0) {
// starts from middle to move right
currentTime = totalDuration / 2;
}
// this condition becomes true only when the
// time spell was rotating forward (left side)
if (previousState != state) {
// change direction of time spell
var newTime = totalDuration - currentTime;
media.currentTime = newTime;
}
}
// when hand is rotated left
function mediaForward() {
// "forward" means original video
state = "forward";
// starts rotating time spell
media.play();
// current time of video
var currentTime = media.currentTime;
// if video stops playing
if (currentTime <= 0) {
// starts from zero to move left
currentTime = totalDuration;
}
// this condition becomes true only when the
// time spell was rotating backward (right side)
if (previousState != state) {
// change direction of time spell
var newTime = totalDuration - currentTime;
media.currentTime = newTime;
}
}
// show next image from images array
function nextImage() {
// increment the index
index++;
// check if the index is out of bound
if (index >= images.length || index < 0) {
return false;
}
// get source of image
var src = images[index].src;
// get text of image
var title = images[index].title;
// update image
document.getElementById("image").setAttribute("src", src);
// update title
document.getElementById("title").innerHTML = title;
}
// show previous image from images array
function previousImage() {
// decrement the index
index--;
// check if the index is out of bound
if (index < 0 || index >= images.length) {
return false;
}
// get source of image
var src = images[index].src;
// get text of image
var title = images[index].title;
// update image
document.getElementById("image").setAttribute("src", src);
// update title
document.getElementById("title").innerHTML = title;
}
Finally, 2 event listeners are added to check when the time spell is rotating and when is paused. Based on that, we will set our variables to change images.
// called when the time spell starts rotating
media.onplaying = function () {
// set the variable to true
setTimeout(function () {
isMediaPlaying = true;
}, 100);
};
// called when the time spell is paused
media.onpause = function () {
// get the previous state
previousState = state;
// set the current state as empty
state = "";
// set the variable to false
isMediaPlaying = false;
};
Following this tutorial, you will be able to track hand rotations using Leap motion controller and Javascript.
Download source code:
[wpdm_package id=’1271′]