Build a web app to test fine motor coordination with Leap Motion : Part 2 (Programming Tutorial)

Building on Part 1 to use a clinically validated test stimulus

--

Introduction

In Part 1 of this tutorial series, HTML, CSS and Javascript was used to build a web application that tracks a mouse cursor position while a user draws a straight line between start and target points. In Part 2, we will build on the existing app by focusing on JavaScript to modify the test stimulus from a straight line to an Archimedes spiral. Prerequisites and Requirements are the same as those in Part 1. I recommend browsing through JavaScript object tutorial

A working demo is available here.

Goal for Part 2 — tracing a spiral with the mouse cursor

Archimedes Spiral

Archimedes studied a particular equation that generated a spiral whose radius varied directly with the angle from the spiral’s origin, and he published the findings in On Spirals in 225 BC (Weisstein).

Spiral equation, in the polar coordinate system: r(θ) = aθ
where
a determines how fast the spiral deviates away from the origin.

The Archimedes Spiral (Weisstein)

The spiral is in use 2200 years later as a test stimulus that has been validated as a method for quantifying essential tremors and Parkinsonian tremor (Memedi et al 2015, Saunders-Pullman et al 2008). In this tutorial, we will draw the spiral in an HTML canvas, and then track the mouse cursor as it traces along the spiral to its target circle.

Step 1. Create the Spiral guide line

Folder Housekeeping

In Mac Finder or Windows Explorer, navigate to your project folder and duplicate your part1 folder and rename the copy part2. Open part2 and rename cursor-line.html to cursor-spiral.html.

Project folder structure

Create a Spiral object constructor

To create the gray spiral guide line, we will make a custom object, or a container for a set of properties and actions (called methods). For example, a Car object could have properties of make, model, color, and methods like goForward, goReverse. Our Spiral object’s properties will include data for starting position, number of complete turns, and methods to generate the set of x,y points for the spiral path. The first step to making a custom object is usually by creating an object constructor, a function used to initialize an object and setup some properties.

Open a new blank document and type or paste the following Spiral object constructor:

The Spiral object constructor, skeleton structure

The params term (short for parameters) found within function Spiral(params){ is another object containing properties that will be passed to the Spiral constructor. The params object will be created in canvas.js in Step 2.

The this. before a property name tells JavaScript “this is a property of this object.” You will be able to access these properties using dot syntax that will be discussed later. Now it is time to add code to the methods of this object. The first, this.xyForPolar, will take an object named coords that contains a polar coordinate {r, angle} and calculate the x,y coordinates in pixels (rounded) using trigonometry. Replace this.xyForPolar = function(coords){ } with the following:

this.xyForPolar() method

this.generateEndPoint() will calculate the x,y position for the final point of the spiral. We will make the blue target sphere position equal to this point later. Replace the empty function with the following:

this.generateEndPoint() method

Notice that we are using this.xyForPolar method and passing it an object with two properties that make a polar coordinate: r (a radius) and angle. The radius is calculated using the spiral equation, r = aθ where a = this.radiusGrowthRate and θ = 360 * this.numberOfLoopsin degrees.

The third method to write is this.generateGuidelinePoints. We will use a for loop to calculate the radius (using the spiral function) at every integer angle from 0 (zero) to the maxAngleInDegrees. These points will be added as properties to the allPoints object which will be made available to canvas.js later.

this.generateGuidelinePoints() method

Save this file as spiral.js in your part2 folder.

Add Spiral object constructor to cursor-spiral.html

The Spiral object constructor is like a kitchen recipe — the instructions are defined, but the meal (or Spiral) does not exist until we create the object elsewhere in the code. Switch back to cursor-spiral.html and import the spiral.js script before canvas.js, then save the html file.

Import spiral.js above canvas.js in cursor-spiral.html

Step 2. Modify canvas.js to draw a spiral guide line

The Spiral object constructor is now available to canvas.js.

  1. Go to canvas.js and create a new object named spiral at the beginning of the $( document ).ready() function as show below. The properties within the new Spiral({ }) are part of the parameters object you pass to the constructor (referred to asparams in spiral.js).
  2. Delete the var startPoint and var endPoint definitions below that, since these positions are now defined as spiral properties.
  3. Create an empty array named radiusPlotForAnalysis to use later.
create a new spiral object

Let’s edit the drawLine() method used to create the guideline layer. In Part 1, we defined x1, y1, x2, and y2, but since the spiral has a very large number of points, we cannot type each coordinate by hand. Instead, we will do this in two steps: create the layer without points, then add all the points as properties using one of the Spiral methods we created earlier. The guideline layer should be simplified to this:

simplified spiral guideline drawLine method

The .setLayer method adds a list of properties to a jCanvas layer, and .drawLayers tells jCanvas to re-draw all the layers with your updated properties.

In the startCircle and targetCircle drawArc methods, there are few properties to update and lines to delete. Look for the changes in the // comments:

startCircle and targetCircle changes needed to upgrade canvas.js to support spirals

Step 3. Tracking the cursor path

Save your files and open cursor-spiral.html in your browser. You should see the cursor tracking just like in Part 1, except it does not automatically clear when the cursor hovers over the green circle again. This is to avoid resetting the canvas if the cursor unintentionally crosses the green circle while drawing the spiral. We can return reset functionality by adding a button and adding code to fire when the button is clicked via the click() method (see Lines 9–11 below). Add the following method after your ‘instructionsTextdrawText method:

Step 4. Analyzing the path

Imagine Archimedes’ spiral was unwound: the shape would turn into a straight line, reminiscent of the guideline path from Part 1.

The canvas.js code adds a data point to the user path every time the mouse moves. If the path were drawn perfectly, each cursor movement along the spiral would increase the radius at a linear rate, because r = aθ. By converting each x,y point in the user path to a radius (from the spiral origin), we can compare how close the increasing radii fit a line by using a least squares linear regression and setting the R² value to be the user’s percent accuracy. The details of this common statistical test can be found here.

Create an Analysis object

We will create a separate object for Analysis methods just as we did for the Spiral object. Open a new blank file, and write the following object constructor skeleton, then save it as analysis.js in the part2 folder.

Analysis object constructor skeleton

Now add code to generate a list of radii from all the points in the path. Replace this.generateRadiiOnSamplesData() with the following lines:

Next, replace this.linearRegression with code below. The method will use all the points inthis.radiiData to compute the regression. The statistical details are beyond the scope of this tutorial but explained here.

The third method to complete is this.printResults:

Here, the R² value is used to create a percentage to represent accuracy. The next line is a jQuery statement to update the contents of<h2 id="results"> in cursor-spiral.html with the new accuracy score.

There are two other optional utility methods also included in the finished file:

  • this.generateCSV outputs the radii into text formatted as comma separated values which can be imported into a spreadsheet (see View -> Developer Tools -> Javascript console to view that output).
  • this.truncateInitialPointsBeforeTrueStart deletes the first set of points recorded in the green circle before the cursor reaches the center of the circle. It can slightly improve the R² value but is not necessary.

Add Analysis to canvas.js

Return to canvas.js to add the analysis functionality to the application. We want to trigger analysis of the path when the mouse cursor moves over the blue target circle. So find the mouseover: property of ‘targetCircle’ layer and add two lines within the if(isTracking) block to create an Analysis object and print results.

Implementing the Analysis object upon reaching the target circle

Import analysis.js to your html

Import analysis.js into cursor-spiral.html with the other script elements. The file must import before canvas.js so that canvas.js knows what theAnalysis structure looks like when trying to create the object.

Step 5. Test your application

Now return to the browser and test your app by refreshing the page. If your canvas is blank, there is a JavaScript error. In this case, check View ->Developer Tools -> JavaScript console to look for error messages that include the line numbers where the error is occurring.

Tip: forgetting to add a comma at the end of a new property within any JavaScript object property list is a common way to break things.

If all is working, you should be able to mouse over the green circle, follow the gray path and reach the blue circle, triggering analysis and printing the result in the HTML page.

Some notes about the analysis limitations

The analytic algorithm is simplified based on the assumption that the user is attempting to follow the spiral. One way to “hack” the algorithm is to draw a straight line from the start to the target. Since that path’s radius would increase linearly with each mouse step, the data would also fit a straight line perfectly. I recommend using more advanced algorithms for analysis if you planned to adapt this project for clinical testing.

Conclusion

In Part 2 we built upon the Part 1 application and dynamically generated a spiral stimulus, tracked a user’s path and performed some basic statistical analysis of the drawing. In Part 3, we will setup Leap Motion controller and change the tracing control from a mouse cursor to a finger in real space.

Continue on to Part 3

--

--

George Marzloff
Association of Academic Physiatrists News

Physician in Spinal Cord Injury & Physical Medicine and Rehab @ Rocky Mountain Regional VAMC, Colorado. Interests: Rehab Engineering & software development