# Import some libraries from PsychoPy and others
import os
from psychopy import core, event, visual, prefs
'audioLib'] = ['PTB']
prefs.hardware[from psychopy import sound
Create your first paradigm
PsychoPy, Python, experimental psychology, neuroscience, tutorial, experiment, DevStart, developmental science
Let’s create our first experimental paradigm using PsychoPy
We will create a very simple and basic experiment that will be the stepping stone for some of the future tutorials. In the future tutorials we will show you how to extend and make this tutorial in a real experiment.
You can download from here the stimuli that we will use in this example. They are very simple and basic stimuli:
- a fixation cross
- two cues (a circle and a square)
- a reward (a cute medal)
- a non-reward (a cartoon of an exploding empty cloud)
- a sound of winning at an arcade game
- a sound of losing at an arcade game
In this tutorial, we will create an experiment in which, after the fixation cross, one of the two cues is presented. The cues will indicate whether we will receive a reward or not and where this will appear. After the circle is presented as a cue, the medal will be presented on the right. After the square is presented as a cue, the empty cloud will be presented on the left. Thus, if you follow the cued indication you will be able to predict the location of the next stimuli and whether or not it will be rewarding. Here below you can find a graphic representation of the design:
Preparation:
First things first, let’s import the relevant libraries and define the path to where our stimuli are. PsychoPy has a lot of different modules that allow us to interface with different types of stimuli and systems. For this tutorial we need the following:
Stimuli:
The next step is to create the window. The window is what we will show the stimuli in; it is the canvas on which to draw objects. For now, we will create a small window of 960*540 pixels. In this way, we will able to see the stimuli and still interact with the rest of our PC interface. In a real experiment, we would probably set the window dimension to the entirety of the display (fullscr=True) and maybe on a secondary screen (screen = 1).
# Winsize
= (960, 540)
winsize
# create a window
= visual.Window(size = winsize, fullscr=False, units="pix", pos =(0,30), screen=0) win
Now let’s import the stimuli that we will present in this tutorial. We have 5 stimuli:
- a fixation cross that we will use to catch the attention of our participants
- a circle that will be our cue that signals a rewarding trial
- a square that will be our cue that signals a non-rewarding trial
- a cartoon of a medal that will be our reward
- a cartoon of an empty cloud that will be our non-reward
On top of these visual stimuli, we will also import two sounds that will help us signal the type of trials. So:
- a tada! winning sound
- a papapaaa! losing sound
When importing a visual stimulus we need to pass to the importing function in which window it will be displayed. In our case, we will pass all of them the “win” window that we just created.
When working with file paths in Python, it’s important to remember that Windows and macOS/Linux use different conventions for their file paths:
Windows Paths:
Windows file paths typically use backslashes (
\
). However, in Python, a backslash is used as an escape character. To handle this, you have two options:Use double backslashes to avoid Python interpreting the backslash as an escape character:
'C:\\Users\\tomma\\Desktop'
Alternatively, use a raw string by prefixing the path with
r
, which tells Python to treat backslashes as literal characters:r'C:\Users\tomma\Desktop'
macOS/Linux Paths:
macOS and Linux use forward slashes (
/
) for their file paths, which are also compatible with Python’s string handling. You can use the path directly. There’s no need for double slashes or raw strings in macOS/Linux paths.'/Users/tomma/Desktop'
Always use the appropriate path format for your operating system to ensure your Python scripts work smoothly across different platforms. We’ll follow this rule in all our scripts, so keep it in mind – it’s a key detail for error-free scripting!
#%% Load and prepare stimuli
# Setting the directory of our experiment
r'C:\Users\tomma\OneDrive - Birkbeck, University of London\TomassoGhilardi\PersonalProj\BCCCD')
os.chdir(
# Load images
= visual.ImageStim(win, image='EXP\\Stimuli\\fixation.png', size = (200, 200))
fixation = visual.ImageStim(win, image='EXP\\Stimuli\\circle.png', size = (200, 200))
circle = visual.ImageStim(win, image='EXP\\Stimuli\\square.png', size = (200, 200))
square = visual.ImageStim(win, image='EXP\\Stimuli\\winning.png', size = (200, 200), pos=(250,0))
winning = visual.ImageStim(win, image='EXP\\Stimuli\\loosing.png', size = (200, 200), pos=(-250,0))
losing
# Load sound
= sound.Sound('EXP\\Stimuli\\winning.wav')
winning_sound = sound.Sound('EXP\\Stimuli\\loosing.wav')
losing_sound
# List of stimuli
= [circle, square] # put both cues in a list
cues = [winning, losing] # put both rewards in a list
rewards = [winning_sound,losing_sound] # put both sounds in a list
sounds
# Create a list of trials in which 0 means winning and 1 means losing
= [0, 1, 0, 0, 1, 0, 1, 1, 0, 1 ] Trials
Note that in this simple experiment, we will present the reward always on the right and the non-rewards always on the left that’s why when we import the two rewards we set their pos
to (250,0)
and (-250,0)
. The first value indicates the number of pixels on the x-axis and the second is the number of pixels on the y-axis.
Show a visual stimulus:
Now we want to show a stimulus in the center of our window. To do so, we will have to use the function “draw”. As the name suggests this function draws the stimulus that we want on the window that we have created.
Let’s start with displaying the fixation cross in the center.
# Draw the fixation
fixation.draw()
Do you see the fixation cross?????? Probably not!! This is because we have drawn the fixation cross but we have not refreshed the window. Psychopy allows you to draw as many stimuli as you want on a window but the changes are only shown when you “refresh” the window. To do so we need to use the “flip” function.
win.flip()
Perfect!!!! The fixation cross is there. Before each flip, we need to draw our objects. Otherwise, we will only see the basic window with nothing in it. Let’s try!!! flip the window now.
# Flipping the window (refreshing)
win.flip()
The fixation is gone again! Exactly as predicted. Flipping the window allows us to draw and show something new in each frame. This means that the speed limit of our presentation is the actual frame rate of our display. If we have a 60Hz display we can present an image 60 times in a second.
So if we want to present our fixation for an entire second we would have to draw and flip it 60 times (our display has a refresh rate of 60Hz)! Let’s try:
for _ in range(60):
fixation.draw()
win.flip()# we re-flip at the end to clean the window win.flip()
Now we have shown the fixation for 1 second and then it disappeared. Nice!! However, you probably have already figured out that what we have done was unnecessary. If we want to present a static stimulus for 1s we could have just drawn it, flipped the window, and then waited for 1s. But now you have an idea of how to show animated stimuli or even videos!!! AMAZING!!!.
Now let’s try to show the fixation for 1s by just waiting.
fixation.draw()
win.flip()1) # wait for 1 second
core.wait(# we re-flip at the end to clean the window win.flip()
Play a sound:
We have seen how to show a stimulus let’s now play the sounds that we have imported. This is extremely simple, we can just play()
them:
winning_sound.play()2)
core.wait( losing_sound.play()
Great now we have played our two sounds!!
When playing a sound the script will continue and will not wait for the sound to have finished playing. So if you play two sounds one after without waiting the two sounds will play overlapping. That’s why we have used core.wait(2)
, this tells PsychoPy to wait 2 seconds after starting to play the sound.
Create a trial:
Now let’s try to put everything we have learned in one place and present one rewarding and one non-rewarding trial:
we present the fixation for 1s
we present one of the two cues for 3s
we wait 750ms of blank screen
we present the reward or the non-reward depending on the cue for 2s.
In the end, we also close the window.
###### 1st Trial ######
### Present the fixation
# we flip to clean the window
win.flip()
fixation.draw()
win.flip()1) # wait for 1 second
core.wait(
### Present the winning cue
circle.draw()
win.flip()3) # wait for 3 seconds
core.wait(
### Present the reward
winning.draw()
win.flip()
winning_sound.play()2) # wait for 1 second
core.wait(# we re-flip at the end to clean the window
win.flip()
###### 2nd Trial ######
### Present the fixation
# we flip to clean the window
win.flip()
fixation.draw()
win.flip()1) # wait for 1 second
core.wait(
### Present the non-rewarding cue
square.draw()
win.flip()3) # wait for 3 seconds
core.wait(
### Present the reward
losing.draw()
win.flip()
losing_sound.play()2) # wait for 2 second
core.wait(# we re-flip at the end to clean the window
win.flip()
# let's close the window at the end of the trial win.close()
ISI
Amazing, we’ve got two trials! But, these trials are back-to-back, which isn’t typically what we want. More often, we prefer a brief gap between trials, known as the Inter Stimulus Interval (ISI). We could introduce this interval by simply adding a core.wait(1)
, which would pause the script for a second. However, I’d like to introduce you to an alternative method to wait for this second, which will be useful in future tutorials.
To implement this ISI, we’ll create a psychopy core.Clock()
. Once initiated, this clock begins to keep track of time, allowing us to check how much time has elapsed since the clock started at any given moment. We’ll then use a while
loop to monitor the elapsed time and break this loop once a second has passed.
The while
loop is a loop that will keep doing the same thing over and over again as long as a certain condition is true, here the passing of 1s.
### ISI
= core.Clock() # start clock
clock while clock.getTime() < 1:
pass
Perfect we can now add this at the end of our trials!!
Stop the experiment
Fantastic, we’ve nearly have our study! However, studies often don’t run to completion, especially when we’re working with infants and children. More often than not, we need to halt the study prematurely. This could be due to the participant becoming fatigued or distracted, or perhaps we need to tweak some settings.
How can we accomplish this? Of course, we could just shut down Python and let the experiment crash… but surely, there’s a more elegant solution… And indeed, there is! In fact, there are numerous methods to achieve this, and we’re going to demonstrate one of the most straightforward and flexible ones to you.
We can use the event.getKeys()
function to ask Psychopy to report any key that has been pressed during our trial. In our case, we will check if the ESC key has been pressed and if it has, we will simply close the window and stop the study.
### Check for closing experiment
= event.getKeys() # collect list of pressed keys
keys if 'escape' in keys:
# close window
win.close() # stop study core.quit()
You can add this check for closing the study at any point during the study. However, we recommend placing it at the end of each trial. This ensures that even if you stop the experiment, you will have a complete trial, making it easier to analyze data since you won’t have any incomplete sections of the study.
Also, you can use this same method to pause the study or interact with its progress in general.
Create an entire experiment
In an experiment, we want more than 1 trial. Let’s then create an experiment with 10 trials. We just need to repeat what we have done above multiple times. However, we need to randomize the type of trials, otherwise, it would be too easy to learn. To do so, we will create a list of 0
and 1
. where 0
would identify a rewarding trial and 1
would index a non-rewarding trial.
To properly utilize this list of 0
and 1
, we will need to create other lists of our stimuli. This will make it easier to call the right stimuli depending on the trial. We can do so by:
# Create list of trials in which 0 means winning and 1 means losing
= [0, 1, 0, 0, 1, 0, 1, 1, 0, 1 ]
Trials
# List of stimuli
= [circle, square] # put both cues in a list
cues = [winning, losing] # put both rewards in a list
rewards = [winning_sound,losing_sound] # put both sounds in a list sounds
Perfect!! Now we can put all the pieces together and run our experiment.
In this final script, we will change the dimension of the window we will use. Since in most of the experiments, we will want to use the entire screen to our disposal, we will set fullscr = True
when defining the window. In addition, we will also change the position of the rewarding and non-rewarding stimulus since now the window is bigger.
If you are testing this script on your laptop and do not want to lose the ability to interact with it until the experiment is finished, keep the same window size and position as the previous lines of code.
# Import some libraries from PsychoPy and others
import os
from psychopy import core, event, visual, prefs
'audioLib'] = ['PTB']
prefs.hardware[from psychopy import sound
#%% Load and prepare stimuli
# Setting directory of our experiment
r'C:\Users\tomma\OneDrive - Birkbeck, University of London\TomassoGhilardi\PersonalProj\BCCCD')
os.chdir(
# Winsize
= (1920, 1080)
winsize
# create a window
= visual.Window(size = winsize, fullscr=False, units="pix", pos =(0,30), screen=1)
win
# Load images
= visual.ImageStim(win, image='EXP\\Stimuli\\fixation.png', size = (200, 200))
fixation = visual.ImageStim(win, image='EXP\\Stimuli\\circle.png', size = (200, 200))
circle = visual.ImageStim(win, image='EXP\\Stimuli\\square.png', size = (200, 200))
square = visual.ImageStim(win, image='EXP\\Stimuli\\winning.png', size = (200, 200), pos=(560,0))
winning = visual.ImageStim(win, image='EXP\\Stimuli\\loosing.png', size = (200, 200), pos=(-560,0))
losing
# Load sound
= sound.Sound('EXP\\Stimuli\\winning.wav')
winning_sound = sound.Sound('EXP\\Stimuli\\loosing.wav')
losing_sound
# List of stimuli
= [circle, square] # put both cues in a list
cues = [winning, losing] # put both rewards in a list
rewards = [winning_sound,losing_sound] # put both sounds in a list
sounds
# Create list of trials in which 0 means winning and 1 means losing
= [0, 1, 0, 0, 1, 0, 1, 1, 0, 1 ]
Trials
#%% Trials
for trial in Trials:
### Present the fixation
# we flip to clean the window
win.flip()
fixation.draw()
win.flip()1) # wait for 1 second
core.wait(
### Present the cue
cues[trial].draw()
win.flip()3) # wait for 3 seconds
core.wait(
### Present the reward
rewards[trial].draw()
win.flip()
sounds[trial].play()2) # wait for 1 second
core.wait(# we re-flip at the end to clean the window
win.flip()
### ISI
= core.Clock() # start clock
clock while clock.getTime() < 1:
pass
### Check for closing experiment
= event.getKeys() # collect list of pressed keys
keys if 'escape' in keys:
# close window
win.close() # stop study
core.quit()
win.close() core.quit()
END
We have our basic experiment and if you have followed up to here you should be able to get along with the basic concepts of PsychoPy!! Well done!!!.