Delays & Randomization

Delays

When you tried your experiment, maybe you noticed that one trial starts immediately after another, which ends as soon as a picture is selected (or the audio ends).
It would probably be a good idea to leave some time both at the beginning and at the end of a trial. To do this, simply use newTimer:

your script should also still contain the welcome trial above this

PennController.Template( 
  variable => PennController(
    newTimer(500)
        .start()
        .wait()
    ,
    newAudio("description", variable.AudioFile)
        .play()
    ,
    newText(variable.Description)
        .unfold(2600)
    ,
    newImage("two", variable.PluralImageFile)
        .settings.size(200,200)
    ,
    newImage("one", variable.SingularImageFile)
        .settings.size(200,200)
    ,
    newCanvas(450,200)
        .settings.add(   0 , 0 , getImage("two") )
        .settings.add( 250 , 0 , getImage("one") )
        .print()
    ,
    newSelector()
        .settings.add( getImage("two") , getImage("one") )
        .settings.keys(          "F"    ,          "J"   )
        .settings.log()
        .wait()
    ,
    getAudio("description")
       .wait("first")
    ,
    newTimer(500)
        .start()
        .wait()
  )
  .log( "ID"     , getVar("ID")    )
  .log( "Item"   , variable.Item   )
  .log( "Ending" , variable.Ending )
  .log( "Group"  , variable.Group  )
)

As you can see, using a template make it very easy to bring general changes to your design.

Note that creating a timer in the cache does not start it, in much the same way that creating a text or an image element does not print it. If you don’t want to freeze your experiment, make sure that a timer has started when a .wait command relating to it is executed.

Randomization

There are at least two things you might want to randomize in your experiment.

The images’ positions

Having a systematic layout for your stimuli is not a good thing if it was not part of your design.
In our case, it could be that participants are in general faster to choose any image that appears on the left (maybe English readers tend to process visual information from left to right and therefore show a reaction time bias) and since we always print our two images on the left, we could misleadingly conclude that people are faster to notice the absence than the presence of an -s.

You can randomly shuffle the positions of all the element contained in a selector using the command .shuffle:

PennController.Template( 
  variable => PennController(
    newTimer(500)
        .start()
        .wait()
    ,
    newAudio("description", variable.AudioFile)
        .play()
    ,
    newText(variable.Description)
        .unfold(2600)
    ,
    newImage("two", variable.PluralImageFile)
        .settings.size(200,200)
    ,
    newImage("one", variable.SingularImageFile)
        .settings.size(200,200)
    ,
    newCanvas(450,200)
        .settings.add(   0 , 0 , getImage("two") )
        .settings.add( 250 , 0 , getImage("one") )
        .print()
    ,
    newSelector()
        .settings.add( getImage("two") , getImage("one") )
        .shuffle()
        .settings.keys(          "F"    ,          "J"   )
        .settings.log()
        .wait()
    ,
    getAudio("description")
       .wait("first")
    ,
    newTimer(500)
        .start()
        .wait()
  )
  .log( "ID"     , getVar("ID")    )
  .log( "Item"   , variable.Item   )
  .log( "Ending" , variable.Ending )
  .log( "Group"  , variable.Group  )
)

It is important that .shuffle gets executed after the images are printed (through the canvas’ .print command above) and added to the selector (otherwise, there is nothing to shuffle) but also before the command .settings.keys is executed: this way, shuffling happens before key assignment, and whichever image ends up to the left and right will be respectively associated with the F and J keys. If .settings.keys were executed before .shuffle then the association would take place before the shuffling. Such a scenario would maybe make sense if you wanted to respectively associate the two and one images with the numeric keys 2 and 1 regardless of where each image ends up on the screen.

The sequence of trials

Manipulating the order in which the trials are presented requires labeling them first. Note that labels are not unique IDs: two trials can share the same label, which we will make use of.

For all the trials you define with PennController(...), you can specify a label using a string as the first argument: PennController( "label" , ... ).

Some trials necessitate a different method. For example, the command PennController.SendResults has no pair of parentheses appended to PennController: instead, you can define a label within the parentheses of SendResults: PennController.SendResults( "label" ).

You can specify your own sequence order using the command PennController.Sequence:

PennController.ResetPrefix(null)

PennController.Sequence( "welcome" , randomize("experiment") , "send" , "final" )

PennController( "welcome" ,
    defaultText
        .print()
    ,
    newText("<p>Welcome!</p>")
    ,
    newText("<p>In this experiment, you will have to report which of two pictures matches a description.</p>")
    ,
    newText("<p>Press the <strong>F</strong> key for the picture on the left, or the <strong>J</strong> key for the picture on the right.</p>")
    ,
    newText("<p>Please enter your ID and then click the button below to start the experiment.</p>")
    ,
    newTextInput("ID")
        .print()
    ,
    newButton("Start")
        .print()
        .wait()
    ,
    newVar("ID")
        .settings.global()
        .set( getTextInput("ID") )
)
.log( "ID" , getVar("ID") )


PennController.Template( 
  variable => PennController( "experiment" ,
    newTimer(500)
        .start()
        .wait()
    ,
    newAudio("description", variable.AudioFile)
        .play()
    ,
    newText(variable.Description)
        .unfold(2600)
    ,
    newImage("two", variable.PluralImageFile)
        .settings.size(200,200)
    ,
    newImage("one", variable.SingularImageFile)
        .settings.size(200,200)
    ,
    newCanvas(450,200)
        .settings.add(   0 , 0 , getImage("two") )
        .settings.add( 250 , 0 , getImage("one") )
        .print()
    ,
    newSelector()
        .settings.add( getImage("two") , getImage("one") )
        .shuffle()
        .settings.keys(          "F"    ,          "J"   )
        .settings.log()
        .wait()
    ,
    getAudio("description")
       .wait("first")
    ,
    newTimer(500)
        .start()
        .wait()
  )
  .log( "ID"     , getVar("ID")    )
  .log( "Item"   , variable.Item   )
  .log( "Ending" , variable.Ending )
  .log( "Group"  , variable.Group  )
)


PennController.SendResults( "send" )


PennController( "final" ,
    newText("<p>Thank you for your participation!</p>")
        .print()
    ,
    newText("<p><a href='https://platform/link.html'>Click here to validate your participation.</a></p>")
        .print()
    ,
    newButton("void")
        .wait()
)

Our command PennController.Sequence( "welcome" , randomize("experiment") , "send" , "final" ) does not change the order much, with the exception of randomize("experiment"). This makes sure that all the trials labeled experiment (all the table-generated trials, see line 32) will be run in a random order.

The list of commands like randomize that you can use within Sequence are listed in the Ibex manual.


Next: R script