Several years ago, I wrote a tutorial for my previous lab on how to create Web-based experiments in Flash. Over the next number of months, I'll be posting that tutorial chapter by chapter.
1. Getting Reaction Time.
For some experiments, you may wish to record reaction time. Reaction time will not be very accurate in a Web-based experiment. A priming experiment, for instance, would probably not be ideal. However, for larger reaction time effects, this methodology should be sufficient.
You could get a reaction time mouse click in the following manner:
In the “probe” frame, add the following code right after the code that displays the probe:
start_time =new Date().getTime();
Add this code to the “same” and “different” buttons, just before the gotoAndPlay statement:
_root.RT = new Date().getTime() - _root.start_time;
The variable “RT” will now contains the number of milliseconds that passed between the probe display and the mouse release. We could change it to the onset of the mouse click by changing the subroutines in the “same” and “different” buttons to on (click).
However, responding with the mouse is slow, and you would probably prefer using key presses. This is more complicated. Do not try to understand the following method. It frankly makes no sense. Just try to learn how to use it.
First, make an empty text box containing a single space. Convert it to a movie clip named “keyTrap”.
Select “keyTrap” and attach the following code:
onClipEvent(keyDown) {
ResponseKey=Key.getCode();
responseTime=new Date().getTime();
_root.RT=responseTime - _root.start_time;
switch (ResponseKey) {
case 83:
//S
if (_root.match==1){
_root.correct = 1;
}else{
_root.correct = 0;
}
break;
case 68:
//D
if (_root.match==1){
_root.correct = 0;
}else{
_root.correct = 1;
}
break;
}
_root.gotoAndPlay("feedback");
}
The onClipEvent(keyDown) subroutine executes when any key is pressed.
Key.getCode retrieves the ASCII key code for the key that was pressed. The table for a standard keyboard is reproduced below.
The only other novel piece of code here is the switch statement. Switch statements in Flash have the following form:
Switch (VARIABLE) {
case VALUE-OF-VARIABLE:
CODE
break;
Case ANOTHER-VALUE
CODE
break;
}
Without the “break” command, Flash will execute the next line. Consider the following code:
Switch (number){
case 1:
trace(‘1’);
case 2:
trace(‘2’);
case 3
trace(‘3’);
}
This code will count to 3 starting with the value of “number”. If “number” = 1, then it will count: 1, 2, 3. If number=3, it will only count: 3.
(the “trace” command outputs to the output window. This can be seen when running a .fla program within Flash. The code does nothing when running off a website.)
You should also delete the “same” and “different” buttons. Now, you can respond by pressing “s” for “same” and “d” for “different”. Your program should now look like this.
ASCII codes for a standard keyboard:
A 65
B 66
C 67
D 68
E 69
F 70
G 71
H 72
I 73
J 74
K 75
L 76
M 77
N 78
O 79
P 80
Q 81
R 82
S 83
T 84
U 85
V 86
W 87
X 88
Y 89
Z 90
0 48
1 49
2 50
3 51
4 52
5 53
6 54
7 55
8 56
9 57
2. The Loader Bar
Ideally, you would like your program to load completely before it starts executing. If it is a large program, this may mean that participants have to wait. It’s a good idea to let them know how long they will be waiting.
This is where a loader bar comes in. I actually don’t understand how loader bars work, so I suggest simply copying the first frame of Part4.fla, including all attached code. This should work. Note that when it finishes loading, it goes to and plays a frame called “Initialize.” You will want to adjust this accordingly.
There are many resources online that discuss loader bars. However, most of them involve loading one Flash file inside of another one.
3. Arrays
As in other programming languages, you will frequently want to make use of arrays. One purpose for arrays is determining the order of all the stimuli before beginning the experiment.
Now that you’ve added a loader bar, your second frame (the one called “Initialize” in Part4.fla) should contain the code
total_trials = 5;
current_trial = 1;
Change it to the following code:
total_trials = 5;
current_trial = 0;
var Stimuli = new Array();
for (i=0; i < total_trials+1; i++){
Stimuli[i]=random(3)+1;
}
var Match = new Array();
Match[0] = 0;
Match[1] = 1;
Match[2] = 0;
Match[3] = 1;
Match[4] = 0;
Match[5] = 1;
The first change is that current_trial is set to 0. This is because the first cell in a Flash array is labeled “0”.
Next, we create an array called “Stimuli”. Using a for loop, we randomly set each cell of “Stimuli” to a number between 1 and 3. There are 6 cells, one for each trial. This way, on trial N, the program cal look to the value of Stimuli[N] in order to choose the stimulus.
You will also want to change the first line of the “stim” frame from
stimulus=random(3)+1;
to
stimulus=Stimuli[current_trial];
Notice that we named the array “Stimuli”. If we named is “Stimulus,” it would then share the same name as the one we assigned to the stimulus movie clip. This causes some of the other code associated with the movie clip to malfunction. So that is something to avoid.
This new code so far does not change how the experiment runs, other than that all the stimuli are chosen in the beginning. This is probably not very useful, but serves as an example.
Next, we create an array called “Match”. We assign half the cells the value 0 and half the value 1. Change the code on the “Probe” frame to the following:
stop();
match=Match[current_trial];
if (match==1){
probe=stimulus;
}else{
ok=1;
while (ok==1){
probe=random(3)+1;
if (probe==stimulus){
}else{
break;
}
}
}
_root.attachMovie("Stim"+probe,"Probe",2);
setProperty(eval("Probe"), _x, 275);
setProperty(eval("Probe"), _y, 200);
start_time =new Date().getTime();
What has changed? Before, we randomly chose a probe, and then set match to 0 if the probe and stimulus did not match and to 1 if they did. Now, we look to the appropriate cell of the Match array. If, on trial N, Match[N] == 1, we set the probe equal to the stimulus. Otherwise, they differ.
The only new code concept is the “while” loop. While loops execute all the code within the {} brackets as while the code within the () parentheses remains true OR until the program executes a BREAK statement. In this case, ok always equals 1, so the while loop will run until it executes the BREAK statement – which can only happen if the probe and stimulus do not match, which is what we want.
The purpose of this code is to make sure that 50% of trials are “Match” trials and 50% are “No Match” trials. However, the pattern is fairly simple, and subjects may catch on quickly. We would like to randomize the order of the trials.
Add the following code to the bottom of the Initialize frame:
var Trials = new Array();
for (i=0; i < total_trials+1; i++){
Trials=i;
}
shuffle(Trials);
function shuffle(a) {
var i = a.length;
while (i) {
var p = random(i);
var t = a[--i];
a[i] = a[p];
a[p] = t;
}
}
First, we create an array called “Trials” and set it to the values 0 through 5. Then we execute a function called “shuffle”, with “Trials” as the argument.
Finally, we define the function “shuffle”. Although this seems out of order, it will work fine. You should be able to parse the code on your own, but basically it uses a simple shuffling algorithm to randomize the order of the cells in Trials. Instead of Trials equaling [0 1 2 3 4 5] it may now equal [3 0 4 5 1 2] or any other random permutation of those values.
How does this help? We need to change one other things. Change the second line of the “probe” frame to:
match=Match[Trials[current_trial]];
I will let you convince yourself that the program will still use each value of “Match” exactly once during the experiment, but rather than running through each cell of Match in order, the order is now randomized.
For consistency, you may wish to make a similar change to the first line of the “stim” frame.
4. Surveys/Demographics
You may want to ask your subjects a few questions. This could in theory be done by using buttons as in the examples above, but it’s less than ideal.
There are several more ways of collecting information from participants. Three include radio buttons, combo boxes and input boxes.
Radio buttons are a group of linked buttons, only one of which can be selected at any one time. Look at the “Demographics” frame of Part4.fla. The first and third questions are both examples of radio buttons.
To set up rradio buttons, first drag a radio button from the Flash components window (or from your file’s library, if it already contains radio buttons). Make as many copies as you need.
Select one of the radio buttons. In the properties inspector, choose “parameters”. There are several you will want to change. “Data” contains the value of that button when selected. In Part4, the “Female” button’s data is “female”, while the “Male” button’s data is “male”. This will be more clear shortly. “groupName” is the name of the group. Radio buttons that share the same groupName are automatically part of the same group. One one can be selected at a time. “Label” is the text that is displayed to the subject. Try changing this and its function will be clear.
A combo box is slightly more complicated to set up. Again, drag a combo box onto your stage. In the properties inspector, change it’s name to age_box. Now, in the code attached to this frame, add the following:
ageData = new Array(101);
ageData[0]="Select";
for (i=1; i<101; i++) {
ageData[i]=i;
}
age_box.setDataProvider(ageData);
This creates an array with the values [‘Select’ 1 2 3 4 … 99 100]. This array is then assigned as the values of the combo box. If you test this frame, you will see that the default for the combo box is the first value (“Select”), and by scrolling you can choose any number from 1 to 100.
The third question is again made of two radio buttons, which have the values of “yes” and “no”. There is one interesting technique used here. The question “Do you have normal or corrected to normal vision?” is actually complicated, and not everybody understands it. However, including the entire explanation involves adding a lot of text, decreasing the chances that the subjects will actually read it.
So I added an invisible button underneath the text “(Click for explanation)”. The text is a dynamic text box called “explain”. The button contains the following code:
on (release){
_root.explain = "If your vision is normal when wearing glasses or contacts, answer 'yes'.";
}
Thus, if the participant clicks in the vicinity of the text box (to be accurate, if they click the invisible button), the text box’s text changes to a brief explanation.
Finally, we have an example of an input box. You create an input box the same as a static or dynamic text box, but in the properties inspector, you choose “input text” instead of “static text” or “dynamic text”. You should type “Initials” into the var box in the property inspector in order to name the data contained in this box, just as you would for a dynamic text box.
You can also type in a default value. Since I want 3 initials, I typed in “XXX”.
Finally, you need a way for subjects to submit their responses, and you need a way of recording that information.
I have included a button called “continue1_btn”, labeled “Continue”. Now, let’s look at the full code for this frame:
stop();
ageData = new Array(101);
ageData[0]="Select";
for (i=1; i<101; i++) {
ageData[i]=i;
}
age_box.setDataProvider(ageData);
demoBtn = new Object();
demoBtn.click = function(evt) {
age = age_box.getSelectedItem();
sex = sex_group.getValue();
normal_vision = vision_group.getValue();
initials = Initials;
good_form = 1;
if (sex == undefined) {
good_form = 0;
}
if (age == "Select") {
good_form = 0;
}
if (normal_vision == undefined) {
good_form = 0;
}
if (initials == "XXX"){
good_form = 0;
}
if (good_form == 1) {
gotoAndPlay("Instructions");
}else{
warning="Please finish all questions before continuing.";
}
}
continue1_btn.addEventListener("click", demoBtn);
The first part we have already gone over. Then we define a new object called “demoBtn”. Then we write an event handler to handle the event that the demoBtn is clicked. It first retrieves the data from the page. “age” is set to the value of the age_box. The value is literally whatever the subject selected. If they selected “74”, then the value is “74”. Then, “sex” is set to the value of the sex_group. Recall that using the property inspector, we set the “data” assigned to one radio button to “female” and the other to “male”. Thus, if “Female” is selected, the value of “sex” will be “female”. “normal_vision” is set by a similar process. Finally, “initials” is set to the value of whatever is typed into the input text box named “Initials”.
It’s important to make sure that the participants filled everything out. We make a dummy variable called “good_form” and set it to 1. Then we check each variable in turn. If it is undefined (in the case of radio buttons) or still set to its default values (in the case of the input text box or the combo box), we set “good_form” to 0.
Finally, if “good_form” = =1, we go on to the next frame. If it does not, we don’t. However, if nothing happens when the subject clicks “continue”, they may not realize it’s because they still have stuff to fill out. They may think the program is broken. So in the case that good_form == 0, we set a dynamic text box to “Please finish all questions before continuing.” That dynamic text box is at the bottom of the screen and is called “warning”. Notice that it must be set as “multiline” in the property inspector; otherwise, not all the text will be visible.
----
The working version of Part4 in .swf form can be found here.
For some experiments, you may wish to record reaction time. Reaction time will not be very accurate in a Web-based experiment. A priming experiment, for instance, would probably not be ideal. However, for larger reaction time effects, this methodology should be sufficient.
You could get a reaction time mouse click in the following manner:
In the “probe” frame, add the following code right after the code that displays the probe:
start_time =new Date().getTime();
Add this code to the “same” and “different” buttons, just before the gotoAndPlay statement:
_root.RT = new Date().getTime() - _root.start_time;
The variable “RT” will now contains the number of milliseconds that passed between the probe display and the mouse release. We could change it to the onset of the mouse click by changing the subroutines in the “same” and “different” buttons to on (click).
However, responding with the mouse is slow, and you would probably prefer using key presses. This is more complicated. Do not try to understand the following method. It frankly makes no sense. Just try to learn how to use it.
First, make an empty text box containing a single space. Convert it to a movie clip named “keyTrap”.
Select “keyTrap” and attach the following code:
onClipEvent(keyDown) {
ResponseKey=Key.getCode();
responseTime=new Date().getTime();
_root.RT=responseTime - _root.start_time;
switch (ResponseKey) {
case 83:
//S
if (_root.match==1){
_root.correct = 1;
}else{
_root.correct = 0;
}
break;
case 68:
//D
if (_root.match==1){
_root.correct = 0;
}else{
_root.correct = 1;
}
break;
}
_root.gotoAndPlay("feedback");
}
The onClipEvent(keyDown) subroutine executes when any key is pressed.
Key.getCode retrieves the ASCII key code for the key that was pressed. The table for a standard keyboard is reproduced below.
The only other novel piece of code here is the switch statement. Switch statements in Flash have the following form:
Switch (VARIABLE) {
case VALUE-OF-VARIABLE:
CODE
break;
Case ANOTHER-VALUE
CODE
break;
}
Without the “break” command, Flash will execute the next line. Consider the following code:
Switch (number){
case 1:
trace(‘1’);
case 2:
trace(‘2’);
case 3
trace(‘3’);
}
This code will count to 3 starting with the value of “number”. If “number” = 1, then it will count: 1, 2, 3. If number=3, it will only count: 3.
(the “trace” command outputs to the output window. This can be seen when running a .fla program within Flash. The code does nothing when running off a website.)
You should also delete the “same” and “different” buttons. Now, you can respond by pressing “s” for “same” and “d” for “different”. Your program should now look like this.
ASCII codes for a standard keyboard:
A 65
B 66
C 67
D 68
E 69
F 70
G 71
H 72
I 73
J 74
K 75
L 76
M 77
N 78
O 79
P 80
Q 81
R 82
S 83
T 84
U 85
V 86
W 87
X 88
Y 89
Z 90
0 48
1 49
2 50
3 51
4 52
5 53
6 54
7 55
8 56
9 57
2. The Loader Bar
Ideally, you would like your program to load completely before it starts executing. If it is a large program, this may mean that participants have to wait. It’s a good idea to let them know how long they will be waiting.
This is where a loader bar comes in. I actually don’t understand how loader bars work, so I suggest simply copying the first frame of Part4.fla, including all attached code. This should work. Note that when it finishes loading, it goes to and plays a frame called “Initialize.” You will want to adjust this accordingly.
There are many resources online that discuss loader bars. However, most of them involve loading one Flash file inside of another one.
3. Arrays
As in other programming languages, you will frequently want to make use of arrays. One purpose for arrays is determining the order of all the stimuli before beginning the experiment.
Now that you’ve added a loader bar, your second frame (the one called “Initialize” in Part4.fla) should contain the code
total_trials = 5;
current_trial = 1;
Change it to the following code:
total_trials = 5;
current_trial = 0;
var Stimuli = new Array();
for (i=0; i < total_trials+1; i++){
Stimuli[i]=random(3)+1;
}
var Match = new Array();
Match[0] = 0;
Match[1] = 1;
Match[2] = 0;
Match[3] = 1;
Match[4] = 0;
Match[5] = 1;
The first change is that current_trial is set to 0. This is because the first cell in a Flash array is labeled “0”.
Next, we create an array called “Stimuli”. Using a for loop, we randomly set each cell of “Stimuli” to a number between 1 and 3. There are 6 cells, one for each trial. This way, on trial N, the program cal look to the value of Stimuli[N] in order to choose the stimulus.
You will also want to change the first line of the “stim” frame from
stimulus=random(3)+1;
to
stimulus=Stimuli[current_trial];
Notice that we named the array “Stimuli”. If we named is “Stimulus,” it would then share the same name as the one we assigned to the stimulus movie clip. This causes some of the other code associated with the movie clip to malfunction. So that is something to avoid.
This new code so far does not change how the experiment runs, other than that all the stimuli are chosen in the beginning. This is probably not very useful, but serves as an example.
Next, we create an array called “Match”. We assign half the cells the value 0 and half the value 1. Change the code on the “Probe” frame to the following:
stop();
match=Match[current_trial];
if (match==1){
probe=stimulus;
}else{
ok=1;
while (ok==1){
probe=random(3)+1;
if (probe==stimulus){
}else{
break;
}
}
}
_root.attachMovie("Stim"+probe,"Probe",2);
setProperty(eval("Probe"), _x, 275);
setProperty(eval("Probe"), _y, 200);
start_time =new Date().getTime();
What has changed? Before, we randomly chose a probe, and then set match to 0 if the probe and stimulus did not match and to 1 if they did. Now, we look to the appropriate cell of the Match array. If, on trial N, Match[N] == 1, we set the probe equal to the stimulus. Otherwise, they differ.
The only new code concept is the “while” loop. While loops execute all the code within the {} brackets as while the code within the () parentheses remains true OR until the program executes a BREAK statement. In this case, ok always equals 1, so the while loop will run until it executes the BREAK statement – which can only happen if the probe and stimulus do not match, which is what we want.
The purpose of this code is to make sure that 50% of trials are “Match” trials and 50% are “No Match” trials. However, the pattern is fairly simple, and subjects may catch on quickly. We would like to randomize the order of the trials.
Add the following code to the bottom of the Initialize frame:
var Trials = new Array();
for (i=0; i < total_trials+1; i++){
Trials=i;
}
shuffle(Trials);
function shuffle(a) {
var i = a.length;
while (i) {
var p = random(i);
var t = a[--i];
a[i] = a[p];
a[p] = t;
}
}
First, we create an array called “Trials” and set it to the values 0 through 5. Then we execute a function called “shuffle”, with “Trials” as the argument.
Finally, we define the function “shuffle”. Although this seems out of order, it will work fine. You should be able to parse the code on your own, but basically it uses a simple shuffling algorithm to randomize the order of the cells in Trials. Instead of Trials equaling [0 1 2 3 4 5] it may now equal [3 0 4 5 1 2] or any other random permutation of those values.
How does this help? We need to change one other things. Change the second line of the “probe” frame to:
match=Match[Trials[current_trial]];
I will let you convince yourself that the program will still use each value of “Match” exactly once during the experiment, but rather than running through each cell of Match in order, the order is now randomized.
For consistency, you may wish to make a similar change to the first line of the “stim” frame.
4. Surveys/Demographics
You may want to ask your subjects a few questions. This could in theory be done by using buttons as in the examples above, but it’s less than ideal.
There are several more ways of collecting information from participants. Three include radio buttons, combo boxes and input boxes.
Radio buttons are a group of linked buttons, only one of which can be selected at any one time. Look at the “Demographics” frame of Part4.fla. The first and third questions are both examples of radio buttons.
To set up rradio buttons, first drag a radio button from the Flash components window (or from your file’s library, if it already contains radio buttons). Make as many copies as you need.
Select one of the radio buttons. In the properties inspector, choose “parameters”. There are several you will want to change. “Data” contains the value of that button when selected. In Part4, the “Female” button’s data is “female”, while the “Male” button’s data is “male”. This will be more clear shortly. “groupName” is the name of the group. Radio buttons that share the same groupName are automatically part of the same group. One one can be selected at a time. “Label” is the text that is displayed to the subject. Try changing this and its function will be clear.
A combo box is slightly more complicated to set up. Again, drag a combo box onto your stage. In the properties inspector, change it’s name to age_box. Now, in the code attached to this frame, add the following:
ageData = new Array(101);
ageData[0]="Select";
for (i=1; i<101; i++) {
ageData[i]=i;
}
age_box.setDataProvider(ageData);
This creates an array with the values [‘Select’ 1 2 3 4 … 99 100]. This array is then assigned as the values of the combo box. If you test this frame, you will see that the default for the combo box is the first value (“Select”), and by scrolling you can choose any number from 1 to 100.
The third question is again made of two radio buttons, which have the values of “yes” and “no”. There is one interesting technique used here. The question “Do you have normal or corrected to normal vision?” is actually complicated, and not everybody understands it. However, including the entire explanation involves adding a lot of text, decreasing the chances that the subjects will actually read it.
So I added an invisible button underneath the text “(Click for explanation)”. The text is a dynamic text box called “explain”. The button contains the following code:
on (release){
_root.explain = "If your vision is normal when wearing glasses or contacts, answer 'yes'.";
}
Thus, if the participant clicks in the vicinity of the text box (to be accurate, if they click the invisible button), the text box’s text changes to a brief explanation.
Finally, we have an example of an input box. You create an input box the same as a static or dynamic text box, but in the properties inspector, you choose “input text” instead of “static text” or “dynamic text”. You should type “Initials” into the var box in the property inspector in order to name the data contained in this box, just as you would for a dynamic text box.
You can also type in a default value. Since I want 3 initials, I typed in “XXX”.
Finally, you need a way for subjects to submit their responses, and you need a way of recording that information.
I have included a button called “continue1_btn”, labeled “Continue”. Now, let’s look at the full code for this frame:
stop();
ageData = new Array(101);
ageData[0]="Select";
for (i=1; i<101; i++) {
ageData[i]=i;
}
age_box.setDataProvider(ageData);
demoBtn = new Object();
demoBtn.click = function(evt) {
age = age_box.getSelectedItem();
sex = sex_group.getValue();
normal_vision = vision_group.getValue();
initials = Initials;
good_form = 1;
if (sex == undefined) {
good_form = 0;
}
if (age == "Select") {
good_form = 0;
}
if (normal_vision == undefined) {
good_form = 0;
}
if (initials == "XXX"){
good_form = 0;
}
if (good_form == 1) {
gotoAndPlay("Instructions");
}else{
warning="Please finish all questions before continuing.";
}
}
continue1_btn.addEventListener("click", demoBtn);
The first part we have already gone over. Then we define a new object called “demoBtn”. Then we write an event handler to handle the event that the demoBtn is clicked. It first retrieves the data from the page. “age” is set to the value of the age_box. The value is literally whatever the subject selected. If they selected “74”, then the value is “74”. Then, “sex” is set to the value of the sex_group. Recall that using the property inspector, we set the “data” assigned to one radio button to “female” and the other to “male”. Thus, if “Female” is selected, the value of “sex” will be “female”. “normal_vision” is set by a similar process. Finally, “initials” is set to the value of whatever is typed into the input text box named “Initials”.
It’s important to make sure that the participants filled everything out. We make a dummy variable called “good_form” and set it to 1. Then we check each variable in turn. If it is undefined (in the case of radio buttons) or still set to its default values (in the case of the input text box or the combo box), we set “good_form” to 0.
Finally, if “good_form” = =1, we go on to the next frame. If it does not, we don’t. However, if nothing happens when the subject clicks “continue”, they may not realize it’s because they still have stuff to fill out. They may think the program is broken. So in the case that good_form == 0, we set a dynamic text box to “Please finish all questions before continuing.” That dynamic text box is at the bottom of the screen and is called “warning”. Notice that it must be set as “multiline” in the property inspector; otherwise, not all the text will be visible.
----
The working version of Part4 in .swf form can be found here.
No comments:
Post a Comment