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.
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 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.
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 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.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:
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.numberOfLoops
in 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.
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.
Step 2. Modify canvas.js to draw a spiral guide line
The Spiral
object constructor is now available to canvas.js.
- 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 thenew Spiral({ })
are part of the parameters object you pass to the constructor (referred to asparams
in spiral.js). - Delete the
var startPoint
andvar endPoint
definitions below that, since these positions are now defined asspiral
properties. - Create an empty array named
radiusPlotForAnalysis
to use later.
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:
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
:
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 ‘instructionsText
’ drawText
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.
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.
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.