Projects

This project started when I needed a way to let participants quickly provide precise ratings of a large number of images on the web. I could have participants click one of several buttons with a mouse or press one of 8 or 10 keys on the keyboard, but these methods were clunky and didn’t allow for a great deal of precision. Instead, I looked into how I might tracking the exact position of the mouse as it moves across a scale, or how I could using joystick or gamepad input.

jsPsych provides a great platform for developing experiments that run in a web browser. It includes plugins for recording responses such as key presses and mouse clicks, but not input from a joystick or gamepad. After some (very messy) cobbling together of code, I got the HTML5 gamepad API working as part of a new jsPsych plugin that allows for gamepad input.

I experimented with other features that eventually made their way into the plugin as well. These include an HTML5 canvas that displays a rating scale from 0 to 100 that shows the current value indicated by the joystick or mouse. The canvas animations can be disabled while still collecting ratings from 0 to 100.

Links to some demo experiments are provided below. Stimuli in the examples include modified and unmodified images from the Chicago Face Database.1 The demos currently use a temporary layout for testing that can be easily modified. The image is presented on the left, and a canvas on the right indicates the current joystick and mouse values, if enabled. Clicking on the scale or pressing the joystick response button (typically the first/primary button) will submit the response and advance to the next trial. At the end of the experiment, data are automatically saved to a server, and there is an optional setting to also save a copy of the .csv data locally.

Variables are passed to the experiment by appending them to the URL.
The subject ID is first passed to the experiment using the ‘?’ sign:

?subject=test1234     #sets subject id to test1234
http://davispsyc.com/joystick/joystick.html?subject=test1234

Additional variables can then be passed to the experiment by adding them with the ‘&’ sign.  Here, we specify that we want to run a “quick test” and only go through 10 trials:

&quickTest=1      #run 10 random trials rather than 50
http://davispsyc.com/joystick/joystick.html?subject=test1234&quickTest=1

Additional variables or options can be specified any of the expressions below. Variables specifying time are in milliseconds.

&useJoystick=1    #enable joystick input
&staticCanvas=1   #disable live canvas updates (joystick and mouse values)
&showDebug=1      #enable joystick debugging
&progressBar=0    #disable progress bar at the top of the page
&localSave=1      #save a local copy of the data as a csv file
&iti=500          #set the inter-trial interval to 500ms (default 100ms)
&lockoutTime=100  #ignore repeating joystick responses for 100ms (default 500ms)
&jsbutton=1       #set joystick response button to 1 (default 0)
&tStim=2000       #display stimuli for 2000ms (default = no limit)
&tResp=5000       #wait 5000ms before automatically advancing (default = no limit)
&hideURL=1        #removes all URLvariables from the address bar after loading
                  #URLVariables will be lost upon refreshing the page if hideURL=1

I’ll upload a copy soon, after attempting to clean up the mess of code I made with the help of countless online resources and tutorials and a good deal of stubbornness.

1Ma, Correll, & Wittenbrink (2015). The Chicago Face Database: A Free Stimulus Set of Faces and Norming Data. Behavior Research Methods, 47, 1122-1135.

#modregplot - moderated regression plotter for continuous variables
#plots X~Y at 1SD above and below the mean of Z
#X is predictor, Y is outcome, Z is continuous moderator

modregplot = function(dataset,X,Y,Z) {
library(ggplot2)
attach(dataset)
a=substitute(dataset)
b=substitute(X)
c=substitute(Y)
d=substitute(Z)

print(noquote(paste(" dataset: ",a)))
print(noquote(paste(" X (IV): ",b)))
print(noquote(paste(" Y (DV): ",c)))
print(noquote(paste(" Z (moderator): ",d)))

modregplot.dataset <<- dataset
modregplot.X <<- X
modregplot.Y <<- Y
modregplot.Z <<- Z

X.uncentered <<- modregplot.X
Y.uncentered <<- modregplot.Y
Z.uncentered <<- modregplot.Z

#uncentered model and plot
uncentered.model<<-lm(Y.uncentered~X.uncentered*Z.uncentered)
uncentered.int<<-coef(uncentered.model)[1]
uncentered.slp<<-coef(uncentered.model)[2]
uncentered.model
uncentered.int 
uncentered.slp

#centered model
X.centered<<-X.uncentered - mean(X.uncentered)
Z.centered<<-Z.uncentered - mean(Z.uncentered)
centered.model<<-lm(Y.uncentered~X.centered*Z.centered)
centered.int<<-coef(centered.model)[1]
centered.slp<<-coef(centered.model)[2]
centered.model
centered.int 
centered.slp

#one SD below the mean of Z (moderator)
X.centered<<-X.uncentered - mean(X.uncentered)
Z.centered<<-Z.uncentered - mean(Z.uncentered)
Z.below.1sd<<-Z.centered + sd(Z.uncentered)
centered.below.1sd.model<<-lm(Y.uncentered~X.centered*Z.below.1sd)
centered.below.1sd.int<<-coef(centered.below.1sd.model)[1]
centered.below.1sd.slp<<-coef(centered.below.1sd.model)[2]
centered.below.1sd.model
centered.below.1sd.int
centered.below.1sd.slp

#one SD above the mean of Z (moderator)
X.centered<<-X.uncentered - mean(X.uncentered)
Z.centered<<-Z.uncentered - mean(Z.uncentered)
Z.above.1sd<<-Z.centered - sd(Z.uncentered)
centered.above.1sd.model<<-lm(Y.uncentered~X.centered*Z.above.1sd)
centered.above.1sd.int<<-coef(centered.above.1sd.model)[1]
centered.above.1sd.slp<<-coef(centered.above.1sd.model)[2]
centered.above.1sd.model
centered.above.1sd.int
centered.above.1sd.slp

#results from lm with centered X and Z
#print('Y~X*Z model with X and Z mean centered')
print(summary(lm(centered.model)))
cat('\n')
print('centered model coefficients')
print(coef(centered.model))
cat('\n\n')
print('centered 1 SD below the mean of Z')
print(coef(centered.below.1sd.model))
cat('\n\n')
print('centered 1 SD above the mean of Z')
print(coef(centered.above.1sd.model))
cat('\n')

###################
#######plots#######
###################

#create readjusted intercept values for uncentered plot
centered.int.adjusted<<-centered.int-(mean(X.uncentered)*centered.slp)
centered.below.1sd.int.adjusted<<-centered.below.1sd.int-(mean(X.uncentered)*centered.below.1sd.slp)
centered.above.1sd.int.adjusted<<-centered.above.1sd.int-(mean(X.uncentered)*centered.above.1sd.slp)

#print centered plot
#print(
#ggplot(dataset) + geom_point(aes(X.centered,Y.uncentered),alpha=.25) + 
#geom_abline(aes(intercept=centered.int,slope=centered.slp, color="mean")) +
#geom_abline(aes(intercept=centered.below.1sd.int,slope=centered.below.1sd.slp, color="-1 SD")) +
#geom_abline(aes(intercept=centered.above.1sd.int,slope=centered.above.1sd.slp, color="+1 SD")) + 
#scale_color_discrete(breaks = c("+1 SD","mean","-1 SD")) +
#labs(x=substitute(X),y=substitute(Y),colour=substitute(Z))
#)

#print uncentered (adjusted) plot
print(
ggplot(dataset) + geom_point(aes(X.uncentered,Y.uncentered),alpha=.25) + 
geom_abline(aes(intercept=centered.int.adjusted,slope=centered.slp, color="mean")) +
geom_abline(aes(intercept=centered.below.1sd.int.adjusted,slope=centered.below.1sd.slp, color="-1 SD")) +
geom_abline(aes(intercept=centered.above.1sd.int.adjusted,slope=centered.above.1sd.slp, color="+1 SD")) + 
scale_color_discrete(breaks = c("+1 SD","mean","-1 SD")) +
labs(x=substitute(X),y=substitute(Y),colour=substitute(Z))
)

#############################
######print plot syntax######
#############################

a=substitute(dataset)
b=substitute(X)
c=substitute(Y)
d=substitute(Z)

# X~Y regression line and confidence bands
# + geom_smooth(aes(X.uncentered,Y.uncentered),method='lm')

#cat('\n')
#print('centered plot syntax')
#cat('\n')

#cat(sprintf('ggplot(%s) + geom_point(aes(X.centered,Y.uncentered),alpha=.25) + 
#geom_abline(aes(intercept=centered.int,slope=centered.slp, color="mean")) +
#geom_abline(aes(intercept=centered.below.1sd.int,slope=centered.below.1sd.slp, color="-1 SD")) +
#geom_abline(aes(intercept=centered.above.1sd.int,slope=centered.above.1sd.slp, color="+1 SD")) +
#scale_color_discrete(breaks = c("+1 SD","mean","-1 SD")) +
#labs(x="%s",y="%s",colour="%s")', c(a), c(b), c(c), c(d)))
#cat('\n')

cat('\n')
print('uncentered plot (readjusted x axis) syntax')
cat('\n')

cat(sprintf('ggplot(%s) + geom_point(aes(X.uncentered,Y.uncentered),alpha=.25) + 
geom_abline(aes(intercept=centered.int.adjusted,slope=centered.slp, color="mean")) +
geom_abline(aes(intercept=centered.below.1sd.int.adjusted,slope=centered.below.1sd.slp, color="-1 SD")) +
geom_abline(aes(intercept=centered.above.1sd.int.adjusted,slope=centered.above.1sd.slp, color="+1 SD")) +
scale_color_discrete(breaks = c("+1 SD","mean","-1 SD")) +
labs(x="%s",y="%s",colour="%s")', c(a), c(b), c(c), c(d)))
cat('\n\n')

detach(dataset)
}