Participant information

Welcome/Instruction screen

There are various good reasons to add a preface page (or even several) to your experiment, including:

  1. the page can describe and give instructions about the experiment to your participants
  2. the page can serve as a consent form
  3. the page can ask the participant to type an ID

In PennController, every page corresponds to a trial, and accordingly has the same structure: To add a page before our picture-selection screen, we simply insert a new PennController(...) in our script above the one we currently have.

For starters, our welcome trial will simply print some text on the page, and invite participants to click a button to start the experiment. Insert the following after PennController.ResetPrefix(null) and before the line PennController( that you already have in your script:

PennController(
    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>Click the button below to start the experiment.</p>")
    ,
    newButton("Start")
        .print()
        .wait()
)

Some explanations: the first command block defaultText.print() tells the script to implicitly insert the command .print immediately below each newText command in the trial. (You can set defaults of all sorts for all types of elements following this pattern.) This way, we don’t have to repeat the .print command for each of the four text elements that we create next in the welcome trial.

As you can see, we wrapped <p></p> around our texts: these are HTML tags indicating paragraphs, which browsers render with top and bottom spacing, making for a nicer visual layout. The <strong> tags print the text in boldface.

The button element is new, but it should be straightforward: parallel to a text element, you create a button in the cache of the trial using newButton and print it using .print. Parallel to a key element, you pause the execution of the trial (which, in our case, would otherwise immediately reach the end) using the command .wait, which in this case listens for a click on the button. Execution resumes when the button is clicked and, since there are no commands left for the trial, the experiment moves on to the next PennController trial, namely the picture-selection screen we created before.

Collecting IDs

You typically will want to have some type of ID associated with each unique participant (e.g., for payment/credit attribution and for purposes of data analysis). You can ask them to type in an ID so you can report it on every line in your results file. (There also is a way to automatically extract IDs from URLs, as certain recruitment platforms will generate URLs containing additional information. See the full documentation for more details, and in particular the command PennController.GetURLParameter.)

To do this, you simply need to create and print a text input element using newTextInput and the command .print. But you also want to retrieve its content and append it to the end of each line in your results file. Here is how to do it:

PennController(
    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") )

Note first that we didn’t use .wait on the text input: we could have done so, and it would have paused the execution of the trial before adding the button to the page, until the participant presses Enter/Return on their keyboard while typing in the input box.

More crucially, we added some code at the end of the trial. After the Start button is clicked, the script executes line 20: newVar("ID"). This creates a var(iable) element in the cache of the trial. The next line, .settings.global(), makes this var element available for all subsequent trials.
Line 22, .set( getTextInput("ID") ) lets the var element store the current content of the text input element.
With no lines left to be executed in this PennController trial, the experiment goes on to the picture-selection trial.

Note that the last line we added, .log( "ID" , getVar("ID") ), appears immediately after the closing parenthesis corresponding to PennController( from line 1. Its effect is to add a value to the lines in the results files generated by this welcome trial. The value that will be added to the results file is the one stored in the var element named ID.
If you test your experiment through the end and refresh your results file, you should see two lines corresponding to your welcome screen: one line for Start and one line for End (automatically reported by PennController). They should contain one more value than the lines we looked at before: that value is what you typed in the input box.

Referencing global vars in later trials

Since we made our var element ID global, every trial after the welcome screen can access it. This means that we can also append the command .log( "ID" , getVar("ID") ) to the closing parenthesis of our picture-selection trial, and the ID value will appear in the results lines of that trial as well. Here is our complete script so far:

PennController.ResetPrefix(null);

PennController(
    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(
    newText("The fish swim in a tank which is perfectly round")
        .print()
    ,
    newImage("two", "2fishRoundTank.png")
        .settings.size(200,200)
    ,
    newImage("one", "1fishSquareTank.png")
        .settings.size(200,200)
    ,
    newCanvas(450,200)
        .settings.add(   0 , 0 , getImage("two") )
        .settings.add( 250 , 0 , getImage("one") )
        .print()
    ,
    newKey("")
        .settings.log()
        .wait()
)
.log( "ID" , getVar("ID") )

Adding a completion screen

Currently, when your experiment is over, a submission confirmation message appears on the screen, but you might want to have a customized final screen for your experiment.

Again, the idea is to create a new PennController trial, but you’d usually want to make sure it appears after the results are saved (so that participants don’t close their browser before data is saved).
You can take manual control over when, during your experiment, the results are sent using the command PennController.SendResults.

For illustration, let’s say you want to show a link to the recruitment platform (e.g., for validation of experiment completion) on your final screen. Here is how you could implement this:

PennController.ResetPrefix(null);

PennController(
    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(
    newText("The fish swim in a tank which is perfectly round")
        .print()
    ,
    newImage("two", "2fishRoundTank.png")
        .settings.size(200,200)
    ,
    newImage("one", "1fishSquareTank.png")
        .settings.size(200,200)
    ,
    newCanvas(450,200)
        .settings.add(   0 , 0 , getImage("two") )
        .settings.add( 250 , 0 , getImage("one") )
        .print()
    ,
    newKey("")
        .settings.log()
        .wait()
)
.log( "ID" , getVar("ID") )


PennController.SendResults()


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

When execution reaches the end of the picture-selection trial, after the F or the J key has been pressed, the script now executes the line PennController.SendResults(), sending the participant’s responses to your results file. Once the results have been saved, the script goes on to the next and final trial, the completion screen, which contains a text element with the HTML <a> tag to generate a link.

We don’t want participants to go beyond this screen, so the last commands to be executed in this final PennController trial set up a little trick to make it impossible to move any furhter: newButton("void"), which creates a button element in the cache of the trial, together with .wait() pauses the execution until the button is clicked. But since .print was never encountered, the button never gets added to the page, and therefore execution never resumes, leaving the text on the page indefinitely.


Next: Audio, Text unfolding and Clicks