Psychtoolbox
We don’t want to run this experiment on the Matlab figure window, since as you can see it is slow and clunky. So we are going to start using a group of Matlab programs developed especially for doing behavioral experiments. Many groups have developed similar sets of programs, but the ones we are going to use are called PsychToolbox.
Contents
- What is Psychtoolbox?
- Downloading Psychtoolbox
- Citing Psychtoolbox
- Getting started with PsychToolbox Screen.m
- Writing code using Screen.m
- FlipScreen.m
- help with Screen
- Screen
- Commands within Screen
- Caveat
- Code comments for FlipScreen
- Calling screen
- Arguments for Screen
- HideCursor
- Pausing
- BlackIndex & WhiteIndex
- FillRect
- Flip
- Close
- If your screen freezes
- Force Quit in Windows
- Force Quit in Mac
- Force Quit in Linux
- FunkyScreen
- Code comments on FunkyScreen
- FunkyJon.m
- Putting up Matrix Images
What is Psychtoolbox?
Psychtoolbox is a free set of MATLAB functions which help researchers carry out vision research on Macintosh and Windows computers. It is a collection of matlab functions written to make presenting visual stimuli easier.It was written by David Brainard, Denis Pelli, Mario Kleiner and Allen Ingling.
The current version of Psychtoolbox is based on OpenGL commands. At its most basic level OpenGL is simply a document that describes a set of functions and the precise behaviours that they must perform. One of the important things about OpenGL is that functions are supposed to work across platforms. From this specification, hardware and software vendors create implementations — libraries of functions created to match the functions stated in the OpenGL specification. Vendors have to meet specific tests to be able to qualify their implementation as an OpenGL implementation. OpenGL is also used in CAD, virtual reality, scientific visualization, information visualization, and flight simulation, as well as (of course) video games.
Downloading Psychtoolbox
You need to download PsychToolbox from here:
http://psychtoolbox.org/
Make sure you download the right version (for MacOSX or PC). Once you have installed PsychToolbox, then you can carry on with this chapter.
Citing Psychtoolbox
Remember to cite the Toolbox. Citing the toolbox isn't just the right thing to do - it's a way of demonstrating that the toolbox is useful so that agencies like the NIH will keep funding it.
"We wrote our experiments in MATLAB, using the Psychophysics Toolbox extensions (Brainard, 1997; Pelli, 1997)."
Brainard, D. H. (1997) The Psychophysics Toolbox, Spatial Vision , 10:443-446. Pelli, D. G. (1997) The VideoToolbox software for visual psychophysics: Transforming numbers into movies, Spatial Vision 10:437-442.
Getting started with PsychToolbox Screen.m
Start with just 1 monitor
Try:
ScreenTest
If you get the screen going blank and then something like the following then screenTest worked
If not, then Psychtoolbox isn't working right on your computer
Writing code using Screen.m
The next stage is also very simple. We are simply going to open an experimental window, making it black, making it white, and then closing it again.
The script below uses a command called Screen, which is one of the core functions of PsychToolbox. Screen is actually not an m file (like the ones you have been writing till now). It is a mex file. This means that it is written in C (or C++) or some other programming language, and is then compiled to run in Matlab. The reason this was done is because Screen does some pretty funky stuff that would be impossible in Matlab.
Oddly, under certain circumstances the Screen command likes to have a capital letter (it is case sensitive unlike most commands in Matlab).
FlipScreen.m
% FlipScreen.m % opens a window using Psychtoolbox, % makes the window black, then white, and then closes % the window again % % written for Psychtoolbox 3 on the PC by IF 3/2007 screen=0; [wPtr,rect]=Screen('OpenWindow',screen, [], []); HideCursor; tic while toc<2 end black=BlackIndex(wPtr); Screen('FillRect',wPtr,black, [100 200 300 700]); Screen(wPtr, 'Flip'); tic while toc<2 end white=WhiteIndex(wPtr); Screen('FillRect',wPtr,black); Screen('FillRect',wPtr,white, [50 50 700 250] ); Screen(wPtr, 'Flip'); tic while toc<2 end Screen('Close', wPtr);
help with Screen
There are two ways of getting help with screen If you type:
help Screen
Screen
You get some general information about screen
If you type:
Screen
Commands within Screen
You get a list of all the subcommands that are contained within Screen. To get more information about a particular command type:
Screen OpenWindow?
Screen DrawText?
Caveat
You should bear in mind that you are entering the murky world of not-quite-professional code. Much of this code is written by people like you, in the middle of trying to do real science. This means that commands may not work as stated, help files may be out of date, commands may not even exist.
Code comments for FlipScreen
screen=0;
[wPtr,rect]=Screen('OpenWindow',screen); Currently the command Screen is taking two arguments.
The first argument is the text string 'OpenWindow' which is telling Screen what you want it to do - open a window.
The second argument tells the command Screen which computer monitor you want to use. If you are running more than one monitor on your computer then 0 means the monitor with the menu bar, 1 means any other monitor. For now, if you have any difficulty I would make sure you are only using one monitor.
Two arguments are returned. wPtr is a handle or window pointer to the window - almost like a variable name that refers to the window. We will use it later to poke things into the window.
rect describes the size of the window. You can see how big your monitor's window is by just typing:
rect
You'll learn more about rect a little later.
when using OpenWindow with Screen, you can use up to 9 arguments. they are listed in the first line of the help file when you type:
Screen OpenWindow?
Calling screen
[windowPtr,rect]=Screen('OpenWindow',windowPtrOrScreenNumber [,color] [,rect][,pixelSize][,numberOfBuffers][,stereomode][,multisample][,imaging mode]);
The square brackets [] mean that a command is optional - if you don't specify it then Screen will use a default value.
Arguments for Screen
- Argument 1. a command telling Screen what to do – in this case you want Screen to open a window.
- Argument 2. Which monitor you want the window opened in. In this case you want the Screen opened in monitor 0 – the one with the menu bar.
- Argument 3. The color you want to fill the window with. So if you wanted to fill the window with red you would change: [wPtr,rect]=Screen('OpenWindow',screen); to [wPtr,rect]=Screen('OpenWindow',screen, [255 0 0]);
Note that instead of using a single number that is an index into the colormap, we are using 3 values - for the red, green and blue guns. There is a weirdness here. If you look at the Display in the control panels you will notice that your monitor probably thinks that it is a 32 bit monitor, but these values are on an 8 bit scale. This is because allowing the red gun to take any number between 0-255 takes up 8 bits, allowing the green gun to take any number between 0-255 takes up 8 bits, allowing the blue gun to take any number between 0-255 takes up 8 bits. 3x8 is 24. The other 8 bits are padding – don’t ask me what they are used for.
- Argument 4. This tells Screen how big you want the window to be. The default is to make the window the size of the entire screen. The order in which you describe the rectangle in which you are placing the screen can be remembered as LeTteRBox (Left, Top, Right, Bottom). If your monitor resolution is 1024 by 1280 then you would want the rectangle to start: 0 pixels from the Left 0 pixels from the Top And go to 1280 pixels towards the Right 1024 pixels towards the Bottom. [wPtr,rect]=Screen('OpenWindow',screen, [], [0 0 1280 1024]); (Why is the first pixel described as 0 instead of 1? Because Screen is a mex file and was written in C which is an 0-based language.) If you wanted a smaller window that was 250 wide and x 100 pixels tall then you would use the following: [wPtr,rect]=Screen('OpenWindow',screen, [], [0 0 250 100]);
- Arguments 5-9. [,pixelSize][,numberOfBuffers][,stereomode][,multisample][,imagingmode]) ; are arguments we aren't going to worry about for now. They will default to the values that we want.
HideCursor
HideCursor
gets rid of the cursor. It will be restored when you close the screen unless you have a crash in the middle. If so, you may need to type ShowCursor in the command window if your cursor remains missing
Pausing
tic while toc<2 end
this simply pauses the program for 2 seconds
BlackIndex & WhiteIndex
black=BlackIndex(wPtr);
white=WhiteIndex(wPtr);
These commands find the color lookup table values (clut) given the screen depth of your computer that will that will give you black or white. Weirdly, the values of white and black depend on the screen depth, especially on Macs
FillRect
Screen('FillRect',wPtr,black); Screen('FillRect',wPtr,white);
FillRect fills a rectangle the size of the screen to be black or white
Screen('FillRect', windowPtr [,color] [,rect] )
You can also specify rgb values for the color Screen('FillRect', wPtr , [255 0 0 ]);
or draw a rectangle that is smaller than the window Screen('FillRect', wPtr [0 255 0 ] [0 0 50 50])
Flip
Screen(wPtr, 'Flip');
Whenever you draw something it is drawn offscreen You then need to Flip the screen so the offscreen window you drew on comes to the onscreen window – the one that is actually on the monitor.
Monitors have a refresh rate of something between 60Hz-120Hz (for a standard monitor). Every refresh begins at the top of the screen, and moves quickly down the screen in a matter of a few milliseconds. The flip command simply tells Matlab to refresh the screen with the image you have drawn offscreen.
Close
Screen('Close', wPtr);
This closes the screen and restores your normal working environment, including your cursor
If your screen freezes
The Screen window will hide the main menu bar and obscure the Matlab command window. That can be a problem if your program stops (perhaps due to an error) before closing the window. The keyboard will seem to be dead because the output of the keyboard is directed to the front most window, which belongs to Screen not Matlab, so Matlab won’t be aware of your typing.
Remain calm.
Force Quit in Windows
Ctrl-c
This halts any program. (Type a "c" while holding down the "Ctrl" key).
Alt-Tab brings the Matlab Command window forward. The screen might still be hard to make out (or invisible), if you’ve been playing with the lookup table. Don't let that worry you. Just type
clear Screen
This will cause Matlab to flush Screen. Screen.mex, as part of its exit procedure, cleans up everything it did, closing all its windows and restoring the lookup table of all its displays.
You might want to type:
clear all clear mex
after killing a program, just to make sure everything is back to normal. clear mex kills any mex files that might be still running
If that didn't work?
Sometimes, Ctrl-C fails to halt progams executing in a Matlab process run with the "-nojvm" option. To halt a runaway Psychtoolbox script in Psychtoolbox you might resort to the Windows Task Manager to kill the Screen program. (Use Ctrl-Alt-Delete to open the Task Manager.)
Force Quit in Mac
Ctrl-c
This halts any program. (Type a "c" while holding down the "Ctrl" key).
Cmd-0 (command-zero; command is the thing that looks like a four leaf clover) brings the Matlab Command window forward. The screen might still be hard to make out (or invisible), if you’ve been playing with the lookup table. Don't let that worry you. Just type
clear Screen
This will cause Matlab to flush Screen. Screen.mex, as part of its exit procedure, cleans up everything it did, closing all its windows and restoring the lookup table of all its displays.
You might want to type:
clear all clear mex
after killing a program, just to make sure everything is back to normal. clear mex kills any mex files that might be still running
If that didn't work?
Sometimes, Ctrl-C fails to halt progams executing in a Matlab process. To halt a runaway Psychtoolbox script in you might resort to Apple-Command-Escape which executes "Force Quit" on Matlab, closing Matlab and all of its windows.
Force Quit in Linux
Ctrl-Alt-Escape , followed by a mouse click kills the onscreen windows and your Matlab session.
You might want to type:
clear all clear mex
after killing a program, just to make sure everything is back to normal. clear mex kills any mex files that might be still running
FunkyScreen
Here is another more elaborate example of how you can use Screen
% FunkyScreen.m % % opens a window using psychtoolbox, % makes the window do some funky things % % written for Psychtoolbox 3 on the PC by IF 3/2007 screenNum=0; flipSpd=13; % a flip every 13 frames [wPtr,rect]=Screen('OpenWindow',screenNum); monitorFlipInterval=Screen('GetFlipInterval', wPtr); black=BlackIndex(wPtr); white=WhiteIndex(wPtr); % blank the Screen and wait a second Screen('FillRect',wPtr,black); Screen(wPtr, 'Flip'); HideCursor; tic while toc<1 end % make a rectangle in the middle of the screen flip colors and size Screen('FillRect',wPtr,black); vbl=Screen(wPtr, 'Flip'); % collect the time for the first flip with vbl for i=1:10 Screen('FillRect',wPtr,[0 0 255], [100 150 200 250]); vbl=Screen(wPtr, 'Flip', vbl+(flipSpd*monitorFlipInterval)); % flip 13 frames after vbl Screen('FillRect',wPtr,[255 0 0], [100 150 400 450]); vbl=Screen(wPtr, 'Flip', vbl+(flipSpd*monitorFlipInterval)); end % blank the screen and wait a while Screen('FillRect',wPtr,black); Screen(wPtr, 'Flip', vbl+(flipSpd*monitorFlipInterval)); tic while toc<1 end % make circles flip colors & size Screen('FillRect',wPtr,black); vbl=Screen(wPtr, 'Flip'); for i=1:10 Screen('FillOval',wPtr,[0 180 255], [ 500 500 600 600]); vbl=Screen(wPtr, 'Flip', vbl+(flipSpd*monitorFlipInterval)); Screen('FillOval',wPtr,[0 255 0], [ 400 400 900 700]); vbl=Screen(wPtr, 'Flip', vbl+(flipSpd*monitorFlipInterval)); end % blank the Screen and wait a second Screen('FillRect',wPtr,black); Screen(wPtr, 'Flip', vbl+(flipSpd*monitorFlipInterval)); tic while toc<1 end % make lines that flip colors size & position Screen('FillRect',wPtr,black); vbl=Screen(wPtr, 'Flip'); for i=1:10 Screen('DrawLine',wPtr,[0 255 255], 500, 200, 700 ,600, 5); vbl=Screen(wPtr, 'Flip', vbl+(flipSpd*monitorFlipInterval)); Screen('DrawLine',wPtr,[255 255 0], 100, 600, 600 ,100, 5); vbl=Screen(wPtr, 'Flip', vbl+(flipSpd*monitorFlipInterval)); end % blank the Screen and wait a second Screen('FillRect',wPtr,black); Screen(wPtr, 'Flip', vbl+(flipSpd*monitorFlipInterval)); tic while toc<1 end % combine the stimuli Screen('FillRect',wPtr,black); vbl=Screen(wPtr, 'Flip'); for i=1:10 Screen('FillRect',wPtr,[0 0 255], [100 150 200 250]); Screen('DrawLine',wPtr,[0 255 255], 500, 200, 700 ,600, 5); Screen('FillOval',wPtr,[0 180 255], [ 500 500 600 600]); Screen('TextSize', wPtr , 150); Screen('DrawText', wPtr, 'FUNKY!!', 200, 20, [255 50 255]); vbl=Screen(wPtr, 'Flip', vbl+(flipSpd*monitorFlipInterval)); Screen('FillRect',wPtr,[255 0 0], [100 150 400 450]); Screen('FillOval',wPtr,[0 255 0], [ 400 400 900 700]); Screen('DrawLine',wPtr,[255 255 0], 100, 600, 600 ,100, 5); vbl=Screen(wPtr, 'Flip', vbl+(flipSpd*monitorFlipInterval)); end % blank the screen and wait a second Screen('FillRect',wPtr,black); Screen(wPtr, 'Flip', vbl+(flipSpd*monitorFlipInterval)); tic while toc<1 end Screen('CloseAll'); ShowCursor
Code comments on FunkyScreen
flipSpd=13;
Here you define flipSpd, you are going to make the display flip every 13 frames.
monitorFlipInterval=Screen('GetFlipInterval', wPtr)
You can find out how fast your monitor flips using 'GetFlipInterval'. 1/monitorFlipInterval will tell you the frame rate in Hz of your monitor.
vbl=Screen(wPtr, 'Flip');
This time when you flipped the window you made Screen return the time (according to the computer clock) that it did the flip. That time is saved as vbl.
Screen('FillRect',wPtr,[0 0 255], [100 150 200 250]);
Here we are using FillRect again, but this time we are defining the rect as only being a subset of the entire screen and we are using a color. Remember that the rect is defined as LeTteRBox.
vbl=Screen(wPtr, 'Flip', vbl+(flipSpd*monitorFlipInterval));
We flip the screen, but this time we are telling it to flip at time | vbl + (13 * monitorFlipInterval)|
This means it will flip 13 frames after the flip where we saved the time of the flip in the variable vbl.
We keep doing the same thing again, but each time vbl refers to the flip that happened on the previous flip. So between each flip there is a 13 frame delay.
Screen('FillOval',wPtr,[0 180 255], [ 500 500 600 600]);
FillOval works just like FillRect except that it draws ovals instead of squares.
Screen('DrawLine',wPtr,[0 255 255], 500, 200, 700 ,600, 5);
DrawLine doesn’t take a rect as an argument. Instead it takes in four separate arguments
- starting horizontal position
- starting vertical position
- ending horizontal position
- ending vertical position I guess the mnemonic for that could be HumVee?
Screen('TextSize', wPtr , 150);
Screen('DrawText', wPtr, 'FUNKY!!', 200, 20, [255 50 255]);
Here we are drawing text on the screen. First we define the size of the text as having a font size of 150. Then we draw it on the screen. 200 and 20 refer to the starting horizontal and vertical positions of where you want the text placed (HumVee again).
[255 50 255] refers to the color of the text.
Screen('FillRect',wPtr,[0 0 255], [100 150 200 250]);
Screen('DrawLine',wPtr,[0 255 255], 500, 200, 700 ,600, 5);
Screen('FillOval',wPtr,[0 180 255], [ 500 500 600 600]);
Screen('TextSize', wPtr , 150);
Screen('DrawText', wPtr, 'FUNKY!!', 200, 20, [255 50 255]);
vbl=Screen(wPtr, 'Flip', vbl+(flipSpd*monitorFlipInterval));
Here we are drawing more than one thing on the offscreen window before we flip it to the front. The order you draw things in matters, since things will be drawn on top of each other.
Here's a version of funkyscreen done by a student.
FunkyJon.m
% FunkyJon.m % This is my Funky li'l m_file written to demonstrate psychtoolbox % % Written by Jon D. Howe 4/16/2007 clear all % Initialize variables screenNum=0; flipSpd=25; % flip every 13 frames [wPtr,rect]=Screen('OpenWindow',screenNum); texHouse=Screen('MakeTexture',wPtr,rect); monitorFlipInterval=Screen('GetFlipInterval',wPtr); black=BlackIndex(wPtr); white=WhiteIndex(wPtr); HideCursor; % blank screen and wait a second Screen('FillRect',wPtr,black); Screen('Flip',wPtr); tic while toc<.1 end % Flip and collect the time of the flip in vbl vbl=Screen('Flip', wPtr); % Draw Background Screen('FillRect', wPtr, [100 255 255]); Screen('FillRect', wPtr, [0 255 0], [0 2*rect(4)/3 rect(3) rect(4)]) vbl=Screen('Flip', wPtr, vbl*(flipSpd*monitorFlipInterval),1); % Draw house Screen('FillRect', wPtr, [0 0 255], [420 300 680 600]); Screen('FillPoly', wPtr, [100 0 0], [420 300; 540 200; 680 300]); vbl=Screen('Flip', wPtr, vbl+(flipSpd*monitorFlipInterval),1); % Draw roof Screen('DrawLine', wPtr, [50 50 50], 410, 310, 540, 200, 5); Screen('DrawLine', wPtr, [50 50 50], 690, 310, 540, 200, 5); vbl=Screen('Flip', wPtr, vbl+(flipSpd*monitorFlipInterval),1); % Draw doors and windows % door Screen('FillRect', wPtr, [200 200 0], [520 450 600 600]); % window Screen('FillRect', wPtr, [175 200 256], [440 380 500 440]); Screen('FrameRect', wPtr, [0 0 0], [440 380 500 440], 3); Screen('DrawLine', wPtr, [0 0 0], 470, 380, 470, 440, 3); Screen('DrawLine', wPtr, [0 0 0], 440, 410, 500, 410, 3); % gable window Screen('FillArc', wPtr, [175 200 256], [520, 260, 600, 320],270,180); Screen('FrameArc', wPtr, [0 0 0], [520 260 600 320],270,180,3); Screen('Flip', wPtr, vbl+(flipSpd*monitorFlipInterval),1); % Draw tree Screen('FillRect', wPtr, [100 100 0], [200 400 230 700]); Screen('FillOval', wPtr, [0 150 0], [50 250 370 550]); vbl=Screen('Flip', wPtr, vbl+(flipSpd*monitorFlipInterval),1); % Draw sun Screen('FillOval', wPtr, [256 256 0], [760 20 920 180]); Screen('DrawLine', wPtr, [0 0 0], 850, 75, 870, 75, 2); Screen('FrameOval', wPtr, [0 0 0], [800 65 820 85], 2); Screen('FrameArc', wPtr, [0 0 0], [800 85 870 160], 90, 180, 2); vbl=Screen('Flip', wPtr, vbl+(flipSpd*monitorFlipInterval),1); % Draw name Screen('TextSize', wPtr, 72); Screen('DrawText', wPtr, 'Funky Jon''s House', 200, 100, [256 256 256]); vbl=Screen('Flip', wPtr, vbl+(flipSpd*monitorFlipInterval),1); tic; while toc<3 end Screen('CloseAll'); ShowCursor;
Putting up Matrix Images
% PutUpImage.m % opens a window using Psychtoolbox, % puts up a 2D image and then a 3D image % % written for Psychtoolbox 3 on the PC by IF 3/2007 screen=0; [wPtr,rect]=Screen('OpenWindow',screen); HideCursor; black=BlackIndex(wPtr); Screen('FillRect',wPtr,black); Screen(wPtr, 'Flip'); tic while toc<1 end image2D=255*rand(100, 100); textureIndex=Screen('MakeTexture', wPtr, image2D); Screen('DrawTexture', wPtr, textureIndex); Screen(wPtr, 'Flip'); tic while toc<2 end image3D=255*rand(100, 100, 3); textureIndex=Screen('MakeTexture', wPtr, image3D); Screen('DrawTexture', wPtr, textureIndex, [], rect, [],0 ); Screen(wPtr, 'Flip'); tic while toc<2 end Screen('Close', wPtr);
Note that putting up a matrix is slightly different from drawing a rectangle or an oval. You can't directly put the matrix onto the window.
There is instead an extra step where you need to create a textureIndex using the Screen command 'MakeTexture'. This textureIndex is an index (this might be a moment to re-read the section on 'references', 'indices', 'handles' and 'pointers') into an OpenGL structure that contains the matrix transformed into an image. Once you have created the texture, you can then draw it on the screen.
Make sure you remember this - it's one of the mistakes that beginners with Psychtoolbox often make.