Wakefield's Daily Tips

This is the first in what I hope will be a daily series of tips. The topic has been closed to prevent discussion. For each tip I will add a link to a thread which can be used instead.

Run the latest version of your experiment

When you synchronise changes to your experiment, you may need to clear your browser cache to see those changes online (using Ctrl-Shift-R, Ctrl-F5, or equivalent). If this does not work (for example because a spreadsheet has changed rather than the JavaScript files or you are using a mobile device) use an incognito (private) browser tab. A participant will not need to do this, so long as they have not already tried a previous version of your experiment.

2 Likes

Naming conventions

In PsychoPy, the names of components, routines, loops and variables must all be unique. For example, you can’t have a text component called text_1 and also use text_1 as the name of the variable that contains the text itself.

One way to reduce the chances of accidentally reusing names is to have a naming convention. For example:

lower_case_with_underscores for components, routines and loops
TitleCase for spreadsheet variables
reverseSentenceCase for code variables

Use variable names that make sense, but do not end names with an underscore and avoid the following (which may work locally but not online).

class, core, frameN (the current frame number), image (except as an image component name), index, Length, list, location, Object, Number, resources, round, sound, Symbol, t (the current time relative to the start of the routine), thisTrial, trials (except as a loop name), util, visual.

I will edit this post if I come across any more.

Use Print statements

When debugging, print statements can be very helpful to track down errors or unexpected behaviour.

I tend to use them in one of two ways:

  1. print('trial setup started')

Sometimes I won’t know where the error is, especially if there is a problem during the initial setup of the components or Begin experiment code. The components of each routine are set up in turn from top to bottom. Therefore, if you have a flow with routines called start, trial and finish and put print('trial setup started') in the Begin experiment tab of a code component at the top of the trial routine then if the text appears in the Stdout tab of the PsychoPy Runner you know that the error isn’t in the setup of the start routine.

  1. print('variableName',variableName)

If you have an error related to the value of a variable or unexpected behaviour due to, for example, an if statement always processing the default else code then the best option is to check the values of the relevant variables. You may discover that your key variable is undefined or is a list when you expected an integer.

A word of warning. Do not put print statements in the Each frame tab of a code component unless you do something to ensure that they aren’t being processed every frame. For example, one way of reducing a print statement to once per second is to use frameN:

if frameN % 60 == 0:
     print('frame',frameN,'variableName',variableName)

Finally, if you want to be able to search the output of your print statements, copy and paste them into a text editor.

Developer Tools

If you encounter an error in an online study, open the Developer tools console using Ctl-Shift-I in Windows/Chrome, Cmd-Opt-J or Cmd-Opt-I in Mac/Chrome, F12 in IE/Edge, Ctrl-Shift-J in Windows/Safari or Ctrl-Opt-J in Mac/Safari.

Select the Console tab at the top and errors item on the left to view errors.

Ignore the 404 error related to favicon.ico

Look for the error that appears on the main screen and click on the link next to Scheduler._currentTask. Take a note of the line number (305 in this case). You may need to refresh the page if the link doesn’t work.

In this case the error is a bug in the mouse store correct function which has since been fixed.

To view the output of print statements (from yesterday’s tip), select the info item from the Console tab (print() translates to console.log();).

Use Auto → JS code components wherever possible

Even if you aren’t planning to run your experiment online, using an Auto code component will help you spot syntax errors in your Python code.

There are some cases where valid Python code comes up as a Syntax error on the JavaScript side but these are rare and, when you encounter then, you can switch to a Both code component and edit or delete the JavaScript side to highlight the fact that they contain code that won’t run online unedited.

Where possible I isolate the lines of code that won’t translate into a separate code component so that most of my code is still being automatically checked and translated.

Reuse your routines

If you are tempted to copy a routine in your experiment because you want another routine to do a similar thing, consider whether you can simply reuse the same routine.

Routines can be reused either by adding them again to your flow or by adding a loop to repeat them.

The advantages of this are:

  1. Your psyexp file will be smaller. Large psyexp files can become unresponsive in Builder.

  2. If you want to make a change to the routine, you only have to do it once rather than changing every copy.

  3. Your flow will be easier for you (and others) to understand.

In some cases you may need to add some code to customise the exact behaviour of the routine, but in most cases it may simply be a case of using spreadsheet variables to alter the presented stimuli/instructions.

Exit cleanly

Locally you might want your participant to end on a thank you screen rather than being sent back to the PsychoPy Builder. Unfortunately, this desire has encouraged a bad habit of leaving the final message on screen and then pressing the escape button to exit.

While data is saved locally after escape has been pressed, until recently microphone recordings were not. My recommendation would be to have the final screen end on a key press that you have not told the participant, e.g. ‘q’.

The more serious issue has been when researchers ask their participants to press escape to end online studies. Online, data is not saved at all unless the experiment ends cleanly. There is an option to save incomplete results, but it does not always work and could be costly if you are paying for credits instead of being covered by a licence.

Code components go at the top

When you add components to a routine they will, by default, appear in the order you create them. This is also, however, the order in which they get processed.

I recommend that when you create a code component you move it to the top of the routine unless you have a specific reason why it should be lower down.

The main reason for this is that it means the code in the Begin Routine tab is processed before any “set every repeat” parameters are set in other components.

Use Embedded Surveys in online experiments

If you are running a study online, you probably want to show your participants a participant information sheet and ask them to consent. You probably also want to ask them some demographic questions. You might even what to present a questionnaire alongside your experiment. Before the Pavlovia Survey routine I used to either use Qualtrics or the VESPR Study Portal to present the participant information sheet and sometimes also sent participants to a separate Qualtrics survey at the end of the experiment. Since 2023 I have been using Pavlovia Surveys instead for most use cases.

Notes

The survey id can’t be a variable so if you want to show different surveys to different participants, you will need to use the Num. repeats parameter of a loop around each survey to determine which ones get presented.

If you use Survey id then the data from a submitted embedded survey will also appear in the responses tab of your survey on Pavlovia.org. However, if you present a survey more than once within the same experiment then only the most data is saved. You will need to explicitly save earlier data within your experiment, especially if the repeated survey is within the same iteration of a loop or you have istrials unticked.

To access your survey data use surveyResponse = surveyRoutineName.getResponse() to copy the responses to a dictionary you can access. To access the response to an individual question you can them use surveyResponse['blockName/questionName']. To check the value for blockName you can print(surveyResponse) and then look at the Browser Tools.

If you need to use an older version of the survey, set the PsychoPy version of your experiment to 2024.1.4. This version only allows for single blocks so you would use surveyResponse['questionName'] instead.

Make sure that your survey doesn’t have a completionURL.

1 Like

Use the error message as the title of your forum post

If you want help on this forum and you can find an error message, then it is the most useful and informative title you can have for your forum post.

Before posting, start by searching for keywords in the error message to see if you can find a previous solution. If you can find a thread which has exactly the same error as you and that solves your issue, click like on the post that helped you. If you still need help, post to that thread instead of making a new one.

If you do need to make a new thread then start with the error message but replace references to specifics with generic terms.

For components, use the default name for that component, e.g. movie.
For loops, use trials (the default loop name).
For resources, use the name of the resource type plus the extension, e.g. movie.mp4.
For spreadsheet variables, use S.
For other variables, use x for floats, i for integers, a for arrays and d for dictionaries.

Make sure that the post itself contains the unedited error message.

Use a stable version

PsychoPy is under constant development, with two new major releases every year. Inevitably some of the new features can introduce bugs elsewhere so you should treat the 20xx.x.0 release as a beta test version and, the final release of each year as the most stable.

Some of my favourite versions are 1.90.3, 2020.2.10, 2021.1.4, 2021.2.3, 2022.2.5 (recommended for eye tracking if you are having issues with the plugins introduced in 2023), 2023.2.3 (recommended for Windows 7), 2024.1.4 (may avoid recent Mac installation issues) and 2024.2.4. If you are using any of them you should be fine, so long as you don’t need later features.

Check your experiment name

The experiment name is in Experiment Settings

When you save a new experiment, this name gets set as the name of the psyexp file (minus the psyexp extension). However, if you create use an existing experiment to create a new one, it will still have the name of the original psyexp file. In some versions of PsychoPy this can cause issues if you try to run the experiment online. The new JavaScript files have the same name as the psyexp file but the indel.html is set to load JS files with the old name. Update the Experiment Name in Experiment Settings and resync to solve the issue.

Know your quotes

If I type:

url = “https://webaddress/?participant=” + expInfo[‘participant’]

and then I type

url = "http://webaddress/?participant=" + expInfo['participant']

and then I copy each one into a PsychoPy code component, the first will give a syntax error and the second will be fine.

The difference is that in the first line, the forum has converted my straight quotes (aka dumb) to curly quotes (aka smart). Curly quotes look nicer in text but do not work for code.

In Python you can use single or double quotes. The recommendation is to use single quotes around short text, but double around sentences, where the text itself might contain apostrophes for possessives or contractions. JavaScript is similar but must use double quotes within dictionaries. The Auto-JS translation in code components converts single quotes in Python to double in JavaScript.

2 Likes

Output path should be blank

In PsychoPy version 2020.1, the files needed for running an experiment online were created in a sub-folder of the experiment folder, usually called html, along with copies of any necessary resources (spreadsheets, media files, etc.).

Since version 2020.2.0 this has no longer been necessary, which saves on server space. More recent versions of PsychoPy will fail with resource errors if you try to use an output path.

Delete the contents of Experiment Settings / Online / Output path (probably html)
Delete the local folder created by the output path setting.
Resync with Pavlovia.

You may need to switch the experiment to inactive and then piloting/running again.

1 Like

Find in experiment

I don’t use the PsychoPy Builder menu items much but this one is particularly useful, especially for tracking down the sources of name conflicts.

This feature is currently in the Experiment menu but is moving to the Edit menu in 2025.1.0. The keyboard shortcut is Ctrl+F.

If you double-click on one of the lines it will open that component.

Note that multiple references in the same code component are not listed separately.

1 Like

The answer is (almost) never to edit the generated code

When you create an experiment in Builder, PsychoPy then generates a Python file which you can use to run the experiment locally and two JavaScript files suitable for running the experiment online (using current and older browsers).

If something doesn’t work, there may be a temptation to make edits directly to the Python/JavaScript files rather than the Builder file. However, this is generally a bad idea for two reasons:

  1. The process is one-way. If you later go back to the Builder file any changes you have made in the generated code will be lost when you generate new Py/JS files.

  2. You are more likely to make a mistake and break something. PsychoPy generates the code according to strict rules, which are not the same as ones you might use when writing an experiment in Coder from scratch.

Sometimes bugs are identified in PsychoPy which can be fixed by editing the generated code until the next release ensures that PsychoPy generates the correct code in the first place.

Let this thread be a warning against manual edits:

If you have an example of a manual edit to generated code that you would recommend, please send me a PM and I’ll add it here.

Know the difference between PsychoJS and jsPsych

PsychoPy was orginally written as a set of Python libraries to support the programming of cognitive psychology experiments, much like Psychtoolbox for MATLAB. However, PsychoPy is also used to refers to the PsychoPy Builder, an experimental generator which allows a user to create experiments in Python using the PsychoPy libraries via a graphical user interface. The Python script generated by PsychoPy Builder can be further customised through the use of code components.

PsychoJS is a parallel set of libraries which allow experiments created in PsychoPy Builder to be translated into JavaScript so that the same experiment can run in a Browser. Any Python code components also have to be translated, though this is often seamless thanks to the Auto->JS transpiler. PsychoJS experiments are almost always hosted on Pavlovia which provides a revenue stream for PsychoPy/PsychoJS/Pavlovia development. Programming in PsychoJS directly is not recommended since most PsychoPy users are more familiar with Python than JavaScript.

jsPsych is not the same as PsychoJS. It is a set of JavaScript plugins which allow someone to extend JavaScript code to incorporate the kinds of features needed for a cognitive psychology experiment. There is a jspsych-pavlovia plugin which allows experiments written in jsPsych to be hosted on Pavlovia, but other options are also available. If you want help with an experiment written in jsPsych, please use the appropriate category. However, I would also recommend that you use their GitHub Discussions area.

1 Like

Builder isn’t Coder

I strongly discourage the use of PsychoPy Coder for the creation of psychology experiments because Builder is easier to debug, share with others and translate into PsychoJS for use online.

If you already have an experiment written in Coder, pasting the code into Builder code components is not sufficient to turn it into a Builder experiment. The logic of how to arrange an experiment is slightly different because of the way Builder works with components, routines and loops.

For example, don’t import packages that are imported as standard by Builder. The following code is at the top of every Builder generated Python script. Specifically, do not import numpy, random or os.

# --- Import packages ---
from psychopy import locale_setup
from psychopy import prefs
from psychopy import plugins
plugins.activatePlugins()
prefs.hardware['audioLib'] = 'ptb'
prefs.hardware['audioLatencyMode'] = '3'
from psychopy import sound, gui, visual, core, data, event, logging, clock, colors, layout, hardware
from psychopy.tools import environmenttools
from psychopy.constants import (NOT_STARTED, STARTED, PLAYING, PAUSED,
                                STOPPED, FINISHED, PRESSED, RELEASED, FOREVER, priority)

import numpy as np  # whole numpy lib is available, prepend 'np.'
from numpy import (sin, cos, tan, log, log10, pi, average,
                   sqrt, std, deg2rad, rad2deg, linspace, asarray)
from numpy.random import random, randint, normal, shuffle, choice as randchoice
import os  # handy system and path functions
import sys  # to get file system encoding

import psychopy.iohub as io
from psychopy.hardware import keyboard

Don’t create a window, unless you are using your experiment across two screens. PsychoPy will create one and call it win.

    if win is None:
        # if not given a window to setup, make one
        win = visual.Window(
            size=_winSize, fullscr=_fullScr, screen=0,
            winType='pyglet', allowGUI=False, allowStencil=False,
            monitor='testMonitor', color=[0,0,0], colorSpace='rgb',
            backgroundImage='', backgroundFit='none',
            blendMode='avg', useFBO=True,
            units='height',
            checkTiming=False  # we're going to do this ourselves in a moment
        )
    else:
        # if we have a window, just set the attributes which are safe to set
        win.color = [0,0,0]
        win.colorSpace = 'rgb'
        win.backgroundImage = ''
        win.backgroundFit = 'none'
        win.units = 'height'

Make use of the variables t (routine timer) and frameN.

    while continueRoutine:
        # get current time
        t = routineTimer.getTime()
        tThisFlip = win.getFutureFlipTime(clock=routineTimer)
        tThisFlipGlobal = win.getFutureFlipTime(clock=None)
        frameN = frameN + 1  # number of completed frames (so 0 is the first frame)

Do not use win.flip(). One is automatically added at the end of each frame. If you are controlling two screens, then you will need to flip the second screen.

        # refresh the screen
        if continueRoutine:  # don't flip if this routine is over or we'll get a blank screen
            win.flip()

Don’t use event.waitKeys(), core.wait() or any other code that interrupts the natural flow of the experiment.

Finally, if you use AI to help you code be aware that you are likely to be given code suitable for Coder rather than Builder.

1 Like

Use the textbox component

The textbox component has been around for a while, but now is the time to start using it instead of the old text component.

Text components sometimes clip off the top or bottom of the text. In English this is rarely important, but in some languages it makes the text unreadable.

New line characters in Excel spreadsheets result in a double line gap online in text components but are fine in textbox components.

Local

Online

Note that the letters themselves are also different when run locally. These two components are both Arial at .1 height units.

Textbox components have more options. The following are not available in text components.

  • Editable
  • Anchor
  • Flip vertical (local only)
  • Overflow options (visible, scroll, hidden)
  • Fill color
  • Border padding / color / width
  • Speech point (local only)
  • Line spacing (local only; works differently for border and fill)
  • Bold / Italic
  • Alignment

Local

Online

Skipping dialogue boxes online

At the start of every PsychoJS experiment you get a dialogue box asking for your participant number, etc. This shouldn’t be hidden and I would recommend that you don’t change the default key “participant” since it gets used in the data file name.

On the other hand you might want your participant to launch the study straight away, especially if you have already assigned a value for participant, etc. using daisy chaining or a custom participation link.

If you want to skip the dialogue box put this code into the Before Experiment tab of a JavaScript code component.

var checkOK = setInterval(function() { //Use basic nested setInterval for Status check of dialog
    console.log("Check if DialogBox loaded");
   if (document.getElementById("progressMsg")) { //Wait for ProgressMsg to exist
        console.log("Dialogbox exists."); //Dialog Box Ready
      var statusText = document.getElementById("progressMsg").innerHTML; //get Status of ProgressMsg
      if(statusText == "all resources downloaded.") //If status is good. Go and let JS Click the button
      {
      console.log("All resources were downloaded! Press Ok now...");
      document.getElementById("dialogOK").click(); // Click on the checkbox
      clearInterval(checkOK);
      }
   }
}, 500); // check every 500ms

What is does it try to press the OK button every 500 milliseconds, so that experiment starts as soon as it is ready. However, your experiment will then launch in a normal browser tab, rather than full screen. The first routine will have to be in a tab, but as soon as you have had user interaction (probably a mouse click or key press) then you can add the following JavaScript code (for example in End Routine)

psychoJS.window.fullscr = true;
psychoJS.window.adjustScreenSize();

You can also use similar code in End Experiment to auto-press the “Thank you for your patience” message after the data has saved, though in this case there isn’t an issue about not being in full screen.

var checkOK = setInterval(function() {

  // Wait for the element containing "Thank you for your patience" to exist
  var thankYouElement = document.querySelector(".scrollable-container");
    if (thankYouElement) {
      console.log("Thank you element found.", thankYouElement.innerText);

      // If the text includes "Thank you for your patience," click the OK button
      if (thankYouElement.innerText.includes("Thank you for your patience")) {
        // Get the status of the OK button
        var statusOK = document.getElementById("dialogOK").innerHTML;
        console.log("Press Ok now...", statusOK);

        // Click on the OK button if it's enabled
        var okButton = document.getElementById("dialogOK");
        if (okButton && !okButton.disabled) {
          console.log("Clicking OK button...");
          okButton.click();
        } else {
          console.log("OK button is not enabled.");
        }

        // Stop the interval
        clearInterval(checkOK);
      }
    }
  }, 100); // Check every 100ms

Of course, if you change the message in this box in Experiment Settings / Online, you will need to change the code accodingly.