Psychtoolbox is a popular MATLAB toolbox for neuroscience and psychology experiments. Building experiments in MATLAB with Psychtoolbox allows a degree of flexibility and precision that is essential in many research niches. However, it can be a technical and time consuming process. Redundancy is also a problem as reusing stimuli and functionality means reintegrating code across different experiments or even reinventing it across labs.
PsychBench is software for MATLAB built on Psychtoolbox. It is a broad suite aimed to make building Psychtoolbox-based experiments faster, easier, and less repetitive—while retaining the flexibility and precision. Work at the level of experiment and stimulus concepts, with more technical control available as needed. An open-source MATLAB + Psychtoolbox library comes with a range of stimulus and functionality types which work anywhere in any experiment. If the library doesn't have what you need, you can add your own. Run experiments locally or export them to run online in VPixx Pack&Go.
This link downloads the MATLAB function pb_install.m. Run it from the MATLAB command line. You can type help pb_install for more information.
Windows / macOS / Linux – 64-bit
MATLAB R2019b or later
PsychBench does not run in GNU Octave.
Psychtoolbox most recent version
Psychtoolbox requires GStreamer (free):
Windows: GStreamer 64-bit MSVC runtime 1.22.5
macOS (Intel and ARM): GStreamer runtime 1.22.5
Linux: generally already installed
You can work with a MATLAB script, spreadsheet tables, or a combination of the two...
In the coding method of making an experiment we use a MATLAB script. That said, it's not really code in the usual sense, more like a list of statements setting parameters. Define each trial by making objects of various types—e.g. picture, sound, keyPress, or more specialized types like linearDotMask, bmlWalker, etc. Any object works anywhere in any experiment. Set properties you want to change from default using dot syntax. You only need to define each distinct trial once, and in any order, so you can use for loops through conditions to do it easily (or multiple for loops in more complex experiments). Finally set a list of distinct trials to run through, adding any repetition and ordering there. PsychBench includes little tools for various kinds of randomization and counterbalancing.
For example, the script below defines three distinct trials. It runs two repetitions of each, all in random order. In each trial a picture of a different shape shows at a height of 8 degrees visual angle and stops showing when the subject responds by any key press. Experiment results report picture file name and response latency for each trial.
<cdcm>% Mark start of experiment script<cdcm>
newExperiment
<cdcm>% Define 3 distinct trials<cdcm>
<cdkm>for<cdkm> fileName = [<cdsm>"sphere.png" "cube.png" "hourglass.png"<cdsm>]
picture = pictureObject;
picture.fileName = fileName;
picture.height = 8;
picture.end.response = true;
picture.report = <cdsm>"fileName"<cdsm>;
recorder = keyPressObject;
recorder.report = <cdsm>"responseLatency"<cdsm>;
addTrial(picture, recorder);
<cdkm>end<cdkm>
<cdcm>% Want 2 repetitions of each distinct trial in random order<cdcm>
nn = randomOrder(rep(1:3, 2));
setTrialList(nn)
<cdcm>% Run experiment<cdcm>
results = runExperiment;
This is just a toy example. Many dozens of properties are available for timing, visual options, response options, options specific to picture and keyPress objects, and more. We can implement any number of conditions of any kind, blocks, intros, breaks, outros, syncs, any kind of ordering, randomization, counterbalancing, etc. Methods like staircases, adjustment, scanners, online experiments, and more are available. For complex experiments you can lay out values in spreadsheet tables and load them into the script. See below for feature highlights.
For more realistic examples, see stroopDemo.m. Or for something more complex wisconsinDemo.m. Or see a list of all demos.
Just for comparison, here is some compact Psychtoolbox code that would run the same experiment as above, with the same timing precision and error handling. The difference in length and complexity is noticeable here and balloons up for more realistic experiments.
<cdkm>try<cdkm>
<cdcm>% Parameters<cdcm>
fileNames = {<cdsm>'sphere.png' 'cube.png' 'hourglass.png'<cdsm>};
pictureHeight_deg = 8;
screenHeight_cm = 17.9;
screenDistance_cm = 55;
iti = 0.75;
<cdcm>% Open on-screen window<cdcm>
[n_window, windowRect] = Screen(<cdsm>'OpenWindow'<cdsm>, 0, [0 0 0]);
flipInterval = Screen(<cdsm>'GetFlipInterval'<cdsm>, n_window);
<cdcm>% Pause keyboard output to MATLAB command line<cdcm>
ListenChar(2)
<cdcm>% Make 3 textures from pictures<cdcm>
<cdkm>for<cdkm> n_trialDef = 1:3
data = imread(fileNames{n_trialDef});
nn_textures(n_trialDef) = Screen(<cdsm>'MakeTexture'<cdsm>, n_window, data);
sourceRect = RectOfMatrix(data);
sourceHeight = RectHeight(sourceRect);
targetHeight = 2*screenDistance_cm*tand(pictureHeight_deg/2)/screenHeight_cm*RectHeight(windowRect);
r = targetHeight/sourceHeight;
targetRect = CenterRect(ScaleRect(sourceRect, r, r), windowRect);
targetRects(n_trialDef,:) = targetRect; <cdcm>%#ok<*SAGROW><cdcm>
<cdkm>end<cdkm>
<cdcm>% Run 2x3 trials in random order<cdcm>
nn_trialDefs = Shuffle(repmat(1:3, 1, 2));
<cdkm>for<cdkm> n_trial = 1:6
n_trialDef = nn_trialDefs(n_trial);
fileName = fileNames{n_trialDef};
n_texture = nn_textures(n_trialDef);
targetRect = targetRects(n_trialDef,:);
Screen(<cdsm>'DrawTexture'<cdsm>, n_window, n_texture, [], targetRect)
<cdkm>if<cdkm> n_trial == 1
t0 = [];
<cdkm>else<cdkm>
t0 = tf+iti-0.5*flipInterval;
<cdkm>end<cdkm>
t0 = Screen(<cdsm>'Flip'<cdsm>, n_window, t0);
[keyIsDown, t] = KbCheck;
<cdkm>while<cdkm> ~keyIsDown
Screen(<cdsm>'DrawTexture'<cdsm>, n_window, n_texture, [], targetRect)
Screen(<cdsm>'Flip'<cdsm>, n_window);
[keyIsDown, t] = KbCheck;
<cdkm>end<cdkm>
tf = Screen(<cdsm>'Flip'<cdsm>, n_window);
latency = t-t0;
results(n_trial,:) = {fileName latency};
<cdkm>end<cdkm>
<cdcm>%Resume keyboard output to MATLAB command line<cdcm>
ListenChar(0)
<cdcm>% Close on-screen window and textures<cdcm>
Screen(<cdsm>'CloseAll'<cdsm>)
<cdkm>catch<cdkm> X
<cdcm>% Close on-screen window and textures on error<cdcm>
ListenChar(0)
Screen(<cdsm>'CloseAll'<cdsm>)
rethrow(X)
<cdkm>end<cdkm>
The full visual method is an option for experiments of low to moderate complexity. The concepts are the same as in the coding method but we lay everything out in tables. You can use any spreadsheet app for this, but Google Sheets allows dropdown menus, quick docs, and starting from examples. When done, save/export to an Excel file, use the command loadExperiment in MATLAB to load it, and runExperiment to run (same as the coding method from that point). For example, the two tables below would make the experiment from above.


PsychBench comes with a library of open-source MATLAB + Psychtoolbox object types.
We add new object types regularly. Please contact us with requests.
If the library doesn't have the stimuli you need for an experiment, you can write your own code in MATLAB + Psychtoolbox. The approach is to write it as a new object type in your library—this allows you to use and reuse the code anywhere in the form of objects, parameterize and get outputs from it in the form of properties, and have it click with all core functionality (e.g. timing, core visual options, results output, working with staircases, etc.). A specialized programming framework and offloading core functionality to PsychBench makes this process efficient. You can make a quick type for just your experiments, or you can make a durable type with more flexibility and a fuller interface for other users. All the standard types that come with PsychBench are open source, so you can use them as examples and/or build off them.
You can share an object type with other people just by zipping its folder and emailing it.
You can also contribute an object type to the library that comes with PsychBench. If you would like to do this, please contact us. We would be happy to credit you. And thank-you!