Fitts's Law Experiment
Authors disclaimer: this work was based on the original Fitts's Law Demonstration done in University Amsterdam (Vrije Universiteit Amsterdam).
The final code for now was made by Igor Santos while at Dalhousie University for the course of Human Computer Interaction in the Faculty of Computer Science, with Professor Kirstie Hawkey.
You can reach me by e-mail, and find additional information on my website, GitHub and BitBucket.
I've added a PHP file that pulls information from the database and presents it, separated by groups, in a simple table and in a CSV file, to be downloaded and used in analysis. This part depends on PHP 5.4, but it's easy to adapt to work on older PHP versions (I'm just using the new array syntax, I think).
The code was adapted to be used by different research groups, and each had at least two different conditions to compare multiple subjects' results.
This way, there's in the first page a form with some fields that should be filled in and the save button should be pressed to create the record in the database and store the information in the session. The fields are as follows:
- group number: used to decide the values that will be used in the tests, to change the balls and number of clicking and etc.
- subject number: used to allow comparison of results between participants and between tests made by the same participant.
- condition number: used to decide exactly what condition to be shown to the participant. There are some times where the only difference between two conditions would be the device/platform used; when that happened, the code had only one condition and presented it every time that group was selected; however, the condition number was still stored, and could be used later to understand in which platform the test was performed.
- details field: this field is currently being hidden as it was used by only one group. It can be used to store additional information about a subject, such as the result in an external test.
Here's where the user will actually be clicking/hovering balls so we can measure up their performance. The test works this way:
- The system loads up two balls: the first one is always the same, and the second one is randomized as explained in the Random Ball Algorithm section later in this document. The first ball gets a "click me" text and the second one is dimmed. By default, the first ball is white and medium-sized, but it can be customized too.
- The user clicks in the first ball, that gets dimmed and the second one is highlighted and receives the "click me" notice. The timer starts counting.
- The user moves and clicks in the second ball. At that time, the timer stops counting, the time elapsed is stored in an array and the click increments a counter of clicks for that type of ball.
- The system repeats this process until all balls have received the minimum amount of clicks, and asks the user to move forward into the Results page.
In this page the system calculates the average time taken between the two balls for each type of ball, stores that in milliseconds in an array and saves all that information to the database, linking with the user information gathered in the first page. As connections can be sometimes unreliable, there's also a button that is presented until the record is saved; so, if the database does not respond for a while, the user can click the button and retry the save operation.
Random Ball Algorithm
At startup time the system creates an array with all the balls' information, including their size and file path. Later on, every time the system needs to present the user a new ball, it calls a function that randomizes those balls and chooses one.
However, if the choosen ball have already hit the maximum number of clicks, the algorithm still allows it to be used twice; that was made to make some room for the randomness code, to prevent the user to be presented with a lot of repeated balls in the end in case the algorithm have shown many balls of similar types in the beginning.
Finally, if the ball have already been clicked the maximum number of times, that ball will be stored in an array of balls that should be avoided in the next random loops. That's why the system warns the user with an alert that the test is over, to prevent them to click in many more balls - what could make the system run out of options to be presented.
The ball settings are also referenced inside the code as "phases". One phase means a ball with a given size, distance and color. Phases are skipped when they reach the maximum number of clicks allowed, and they are picked by the Random Algorithm. There's a variable called
NPhases in the code that contains the same number of balls available for a given condition, and there's also some internal variables used for controlling the test and storing relevant information about the balls, such as
The code have those files:
index.html: loads an iframe with the preset size and
inside.html: works as a sandbox/session for the experiment, saving all the useful information and helping on switching pages
1.html: welcome page, with the data form
2.html: a page with a quick explanation about the test
3.html: actual test page, where everything happens
4.html: results page, where stuff is saved
display.css: where the style for the test is. There's some in-file CSS in the other HTML pages, too
db.js: JS object that communicates with the database
fitts.js: the actual test logic, with everything that is customizable and keeps the ball rolling (lol)
data.php: retrieves and organizes the information from the database to be presented to the researchers
intr/: images for the background, buttons, a script used to download the original balls and all the balls available for usage. Those balls are named as follows:
Available settings and other variables
You can find everything that's customizable in the test in the beginning of the
fitts.js file. The information is currently stored in an array for the groups, but it can be easily changed to have fixed settings (without different groups) and still maintain different conditions. Here are the possibilities:
ballData works as an easier way to write information about the balls. This object should be used in the groups' settings. It contains the available:
- sizes: 10, 20 and 40px of diameter;
- distances: near, medium and far - float numbers used to calculate the number of pixels between the first and the second ball;
- colors: there are two hashes here:
- default colors, that are numbered, being white the default color for the first ball and blue/green/red/gold colors for the second ball;
- special colors, that have a different, more complex organization (used by one of the groups as the colors dictated the condition, and each condition had three different sets of hue/luminosity).
There's also an array called
randomBalls that's the default setting for a random set of balls; it contains all combinations of size and distance, with the colors not being important and were randomly assigned to those pairs. As this is a frequent choice for tests, it was created separately to DRY up the code.
This is divided into two parts: the actual array of groups, and a function that makes it easier to pick a setting or choose a default value for it if the group does not provide it.
Here's the list of options that the
groupSets array can contain. Options with a
~ before were not tested enough, or at all, and those with a
$ where not ported into this array (just exist in the global scope, under this array) but can be done too.
clicks [int]: number of clicks per ball/set (see
maxBalls [int]: number of balls each condition has
totalClicks [bool]: if the number of clicks should be counted for each ball or for all the balls as a whole.
Example: if the set have
totalClicks=falseit means the user will click at least 9x6 times, while having
totalClicks=truemeans the user will need to click exactly 50 times, no matter how many times each ball has appeared (conflicting with the randomness of the balls).
experimentNo [int]: the type of experiment that will happen. The default is 4 and I'm not sure about what will happen if this value is changed. In the original code, experiment 1 had same distance/size balls, 2 had different distances and 3 had different sizes, while 4 had both changing and 5 had different input devices. All the changes made were considering only
multipleConditions [bool]: whether the group have multiple code conditions or if the change is only in the test environment, not in the balls (changing the type of balls versus the input device between conditions, for example).
firstBall [hash]: information for the ball that initiates the timer:
width: the ball size (i.e.
color: the color it should be presented (i.e.
balls [array of hashes]: the final array with the random ball information. There should be as much elements as the number in the
maxBallsvariable. Those are the available settings:
width: same as
color: same as
distance: the distance from the first ball (i.e.
pointing [bool]: if the user should click or just point to a ball. Defaults to
needToClickOnStart [bool]: if there's a need to click in the first ball or not. Defaults to
needToClickOnEnd [bool]: if there's a need to click in the second ball or not. Defaults to
parkTime [int]: milliseconds taken to mark a ball as "clicked" if one of the above settings for not-clicking is enabled. Defaults to
nballs [int]: probably the number of balls presented at the same time in the screen, plus the initial ball. Defaults to
ShadowMargin [int]: probably used to create a padding between the border and the balls
FieldWidth [int]: width of the test field. Should be changed in the main iframe too.
FieldHeight [int]: height of the test field. Should be changed in the main iframe too.
The information stored by the system is saved in the end of the test in a cloud database called MongoLab. It's a NoSQL (MongoDB) Database-as-a-Service that offers a REST API with theirs plans, including the free plan.
However, the code currently have the key for my academic account on MongoLab, giving you access to any data stored in there. No, there's nothing sensitive there. In fact, I'm probably going to shut down the account later, as it's of no use for me after the university. So, I would suggest you to fork this code (or download it) and change the key to another account's key, so you can guarantee your own access and reliability of the information. Oh, and if you change anything in your code, please send a pull request later (:
The data is saved by a database object created in the
db.js file. It communicates with the REST endpoint and does very basic operations. It can be expanded to store incremental information (to store data after each test in a multi-test system) easily. Read the API documentation.
Code and architecture
Something I wanted to do but had no time for was improving the JS code to get rid of all those global variables and functions. It was troublesome to write new code sometimes because of the conflicting variable names in the global scope, and this could be a first step of modification if anyone is willing to make deeper changes in the system.
I would also do some extra work with the arrays and such in the main code, since there's a lot of weirdness using array keys to find and store information, instead of using hashes for that. Example: to store the number of clicks done in each ball the code uses the
Trials array, that stores the index of each ball and their respective number of clicks; however, the balls indexes starts at 1, making the first element in the array empty. This could be confusing and may lead to some headaches later on, if you try to loop in the values for example. There are other places where hashes/objects would be better used instead of arrays.
Would be nice to have an GUI for the researchers to choose the settings they want. The information is currently stored in an array inside the test JS file, but that can easily be retrieved from the NoSQL database - and in the same way, there could be a form with the relevant fields, that would store the chosen settings in the database and they could be retrieved later.
It lacks to the system interface some unification, since sometimes the test warns the user it's over using an alert (probably because going much further would break the system, I don't remember exactly but there are some filters on the number of clicks available for each ball, and if the user goes over that number for all balls there would be problems), and sometimes the warn happens with the original red arrow pointing to the next button. This also happens between the data saved in the first and the last page.
The first form should be saved when the user clicks in the next button instead of a separate button, too.
The code also covers some other possibilities for testing that were not used, and thus, may have not been ported to settings arrays and were not used:
Clicking versus Hovering/parking
There's the ability to mark a ball as clicked if the user simply pass the mouse over it and stops for some time. It can be useful for testing speed of mouse/trackpads, not considering the technology used for clicking (taps, button press, pushes in the pad (Macs) or trackball press). This possibility is related to the variable
ParkTime(amount of time needed to count as "successfully hovered") and
NeedToClickOnEnd, that indicates whether the timers should start/end with a click in a ball or after the amount of time indicated by
ParkTimehave elapsed. There's also the boolean
Pointingthat should serve to a similar purpose, although I'm not sure about its relation with the other pair of booleans.
Two input devices in the same test
The original test had a phase that the user should change the input device before trying a second time. This is identified in the code as Experiment number 5, and was by and large removed from the code base. It felt better to have totally different tests happening, with clearly different condition data, so it would be easier to store the information and compare later. However, you can look at old versions of the code to see how the Experiment 5 was handled.
There are two variables,
FieldHeightthat are used to understand the workspace space and draw the balls. It may be possible to create a fullscreen application dinamically changing those variables, but some work would probably be needed in the HTML/CSS too to support those aesthetic changes.
There are some variables in the code that I'm currently unsure about their usage/value:
ClicksLastExperiment: no idea what this is.
ShadowMargin: probably used to allow some space between the field end and the balls?
NBalls: looks related to the number of balls presented at the same time in the screen, but I have no idea how this could be used, nor the system's behavior with different settings here
Mode: I'm unsure about part of the usage of this variable, but it's used to understand the state of the test: if the user have yet to click the first ball, if it's on his way to the second ball, etc. Take a look at the comments for this variable to undestand more of it.
The random algorithm also calculates the position of the first ball and the angle+distance of the second ball. This could be tweaked to have, in example, a Fitts's Law test regarding screen edges, or thumb usage on mobile devices - balls appearing at opposing sides are probably easier to be touched with the thumbs in a mobile phone than if they are in the same size of in the center.