Key Presses

 

Here we will go through the various ways that you can collect keypresses and discuss their strengths and weaknesses. Different techniques are useful for different purposes. This summary list should help you keep these commands straight in your head.

pause

Doesn't work in Psychtoolbox Doesn't provide precise timing Doesn't record which key was pressed Does wait for keypress or for a specific period of time

input

Doesn't work in Psychtoolbox Doesn't provide precise timing Does record which key was pressed (need to pre-specify number or string) Does wait for keypress

CharAvail

Does work in Psychtoolbox Doesn't provide precise timing Doesn't record which key was pressed Doesn't wait for keypress

Simply checks whether there is a key press in the event queue

GetChar

Does work in Psychtoolbox Does provide precise timing (with dubious accuracy, see help) Does record which key was pressed Does wait for keypress

Checks or waits for a key press in the event queue. Returns what the key was, and when it was pressed

KbCheck

Does work in Psychtoolbox Does provide precise timing Does record which key was pressed Doesn't wait for keypress

Tests whether a key has been pressed at that moment in time

KbWait

Does work in Psychtoolbox Does provide precise timing Doesn't record which key was pressed Does wait for keypress

Waits for a key press to occur

Ways to collect keypresses when not using psychtoolbox

pause and input

These techniques are useful when you are not using Psychtoolbox. Remember that none of these techniques have good timing. It’s easiest practicing these commands in a m-file, since many of these commands will accept Return as a key press (if you type them into the command line, the Return you use at the end of the line will also be used as the input into the key press command).

pause

 
pause

This is the simplest command you can use to collect a key-press. It simply pauses the computer until a key (any key) is pressed on the computer and doesn’t collect what that response is.

Bear in mind that the command window needs to be in front for the keypress to be available to Matlab, so pause doesn’t work well if you are using Psychtoolbox. In that case use GetChar instead.

pause can also be used to enforce a delay –

pause(3)

pauses for 3 seconds.

input

input

This command again pauses the computer until a key is pressed but it allows you to collect the response. By default the response is a number

resp_num=input('press a number key ...');  

But you can also specify that input will accept a string, as shown below. In that case if the subject inputs a number key the computer will assume that the subject chose the character ‘3’ rather than the double 3.

resp_char=input('press a number key ...', 's');

whos  

Note that resp_num is a double and resp_char is a character.

If in a program you want to only accept a certain type of response, then you need to write a loop like this one:

PracticeKeyPresses

 
% PracticeKeyPresses
% a program to practice different ways of collecting
% key press responses
%
% written if 4/2007
 
% using input
disp('Using input command');
resp='x';
while resp~='a' && resp~='b'
    resp=input('press a or b ... ', 's');
end
 

disp(resp);  

 

Using input command

press a or b ...   

 

Bear in mind that the command window needs to be in front for the key-press to be available to Matlab, so, like pause, input doesn’t work well if you are using Psychtoolbox. In that case you will need to use GetChar, CharAvail or KbCheck instead (see below).

Collecting keypresses using GetChar & CharAvail

When you type into the keyboard (or any input device) the key presses get saved into an mysterious store in the bowels of the computer called event queue. This is why sometimes when you type really fast and the computer is also busy with other tasks there will be a pause before the letters suddenly appear in your document. CharAvail and GetChar commands read from the event queue. It’s therefore important to empty events from the event queue before collecting responses using these two commands. You do this using the command:

FlushEvents
 
Listen Char
This is a function that tells Matlab to stop (ListenChar(2)) or start (ListenChar(0))  listening to keyboard input. Add this at the very end of progamming your experiment since you should use it with care: if your script aborts with an error, Matlab will be left with a dead keyboard until you press CTRL+C to reenable keyboard input. 
 
GetChar, CharAvail, KbWait and KbCheck will still work. The most useful thing about ListenChar is that it suppresses output to the command window. 
 
 

CharAvail

CharAvail looks to see if there is anything in the event queue.

[avail, numChars] = CharAvail;

avail will be 1 if characters are available, 0 otherwise. numChars may hold the current number of characters in the event queue, but in some system configurations it is just empty, so do not rely on numChars providing meaningful results unless you've tested it on your specific setup.

If avail is 1 then you need to call GetChar to find out what the actual key press is – CharAvail just tells you whether there was a key press.

CharAvail

 

WaitSecs(1)

PsychJavaTrouble

% if commands like ListenChar, CharAvail GetChar or FlushEvents fail

% type help PsychJavaTrouble

% - no. 3 is the most common issue

 

 

disp('Using CharAvail command')

disp('Wait 2 sec to see if you pressed a key during that time');

FlushEvents

ListenChar(2);

tic

while toc<2

end

resp = CharAvail;

if  resp==1

    disp('You pressed a key');

else

    disp('You didn''t press a key');

end

ListenChar(0); 

 

CharAvail is useful when you want to see whether your subject pressed a key while you were doing something else (such as displaying images) in the meantime. It’s especially useful if you are doing something like fmri where you don’t want to wait indefinitely for a subject response.

GetChar

GetChar If there is something already in the event queue then GetChar retrieves it. If there isn’t something in the Event queue then GetChar waits for a typed character and lets you save the response.

%
% |GetChar| is used as follows:
[resp, when] = GetChar(getExtendedData, getRawCode);

char is the character that was typed when is a structure. It returns the time of the key press, the "adb" address of the input device, and the state of all the modifier keys (shift, control, command, option, alphaLock) and the mouse button. If you have multiple keyboards connected, address may allow you to distinguish which is responsible for the key press. when.secs is an estimate of the time when the key press happened. However the timing of GetChar is not necessarily reliable, the reported values can be off by multiple dozen or even hundreds of milliseconds. If you are interested in precise timing you should check the timing of GetChar on the particular system that you are using or use KbWait or KbCheck instead.

getExtendedData tells GetChar whether or not to collect when data, if set to 0 then GetChar will be a tiny bit faster. getRawCode determines whether you want to return the character information in ascii or char (the default) format. Normally you won’t bother using either of these input arguments.

Add the following lines to your PracticeKeyPresses m-file. If you press a key GetChar behaves very like CharAvail except that it retrieves what the character is. If you don’t then GetChar waits

PsychJavaTrouble

 

WaitSecs(1)

disp('Using GetChar command just on its own');

disp('Waiting 2 sec to see if you pressed a key during that time ...');

 

FlushEvents

ListenChar(2);

 

tic

while toc<2

end

[resp_GC, when_GC]=GetChar;

 

disp('You have pressed a key')

disp(['The key was ... ', resp_GC]);

ListenChar(0);  

 

Note that even though the command line may appear, the program doesn’t actually continue to print 'now you have pressed a key' until you press a key – if there’s nothing in the event queue then GetChar will wait for a response.

What if you only want to accept certain responses (e.g. the ‘m’ or ‘f’ keys)?

GetChar - only accepting certain responses

 

WaitSecs(1)

disp('Using GetChar command - only accepting certain responses.');

disp('Press ''m'' or ''f''')

FlushEvents

ListenChar(2);

resp_GC2='x';

while resp_GC2~='m' && resp_GC2~='f'

    disp('press m or f ... ');

    resp_GC2=GetChar;

end

disp('now you have pressed an acceptible key')

disp([' the key was ... ', resp_GC2]);

ListenChar(0);  

 

 

Combining GetChar and Char Avail

What if you want to combine the useful property of CharAvail that it doesn’t wait indefinitely for a response with the useful property of GetChar that you can find out what the response was?

WaitSecs(1)

disp('Combining GetChar and Char Avail')

disp('Wait 2 sec to see if you pressed a key during that time');

FlushEvents

ListenChar(2);

tic

while toc<2

end

resp_CA3= CharAvail;

if resp_CA3==1

    resp_GC3=GetChar;

    disp(['You pressed the key ', resp_GC3])

else

    disp('You did not press a key');

end

ListenChar(0);  

 

 

Collecting keypresses using KbCheck & KbWait

KbCheck and KbWait don’t pull key presses out of the event queue. Instead they monitor the state of the keyboard at that very moment. The advantage of this is that they tend to have more accurate timing on some systems, and there is no lag – the minute the key press occurs the KbCheck and KbWait command knows that the key press has happened. The disadvantage is that it’s sometimes hard to do something else (e.g. display images) while constantly monitoring the state of the keyboard.

One thing that is a little weird about KbCheck and KbWait is that they actually refer to the key that was pressed on the keyboard rather than the character represented by that key. This is because they are lower level commands. What character a key represents actually varies across computer systems, which means you need to convert your key press information into character information.

While most keynames are shared between Windows and Macintosh, not all are. Some key names are used only on Windows, and other key names are used only on Macintosh. For a lists of key names common to both platforms and unique to each see the comments in the body of KbName.m.

KbName will try to use a mostly shared name mapping if you add the command KbName('UnifyKeyNames'); at the top of your experiment script. In fact, KbName often won’t work unless you add this line to the beginning of your scripts!

Psychtoolbox has a great demo for KbCheck and KbWait which is called KbDemo. You can try it by just typing KbDemo at the command window:

KbDemo

KbDemo is also a good way of making sure that KbCheck is working. If not, try replacing the version of KbCheck.m that you have with a revised version found here:

<http://en.wikibooks.org/wiki/Psychtoolbox:KbCheck>

The examples below are simplified versions of taken from KbDemo

Pre-loading mex files

One more thing to remember: KbCheck and KbWait are MEX files, which take time to load when they're first called. They'll then stay loaded until you flush them (e.g. by changing directory or calling CLEAR MEX). So your timing on the first trial will be more accurate if you just call them at the beginning of the program.

KbCheck

Checks to see whether a key on the keyboard is pressed down at that very moment in time.

[keyIsDown,secs,keyCode] = KbCheck;

keyIsDown is 1 if any key, including modifiers such as shift,<control> or lock is down. 0 otherwise.

secs is the time of the key press as returned by GetSecs. This is an accurate way of getting timing but you should still read the help file for KbCheck and GetSecs carefully if your experiment depends on accurate timing.

keyCode On Macs this is a 128-element array, on PCs it is a 256 . Each number in the array represents a keyboard key. If a key is pressed that index in the array is set to 1, otherwise it will be set to 0. To convert a keyCode to a vector of key numbers that were pressed use find(keyCode). To find out what actual key that was use KbName.

KbWait

Just like KbCheck, but it waits until a key on the keyboard is pressed down and simply returns the time that the key was pressed.

secs = KbWait;

secs is the time of the key press as returned by GetSecs.

KbName

KbName maps between KbCheck-style keyscan codes and the character that key represents. Use KbName to make your scripts more readable and portable, since keycodes, are cryptic and vary between Mac and Windows computers.

arg=100;
kbNameResult = KbName(arg)

KbName actually will let you go either way from keycodes to characters, or vice versa. If you send in as input a string designating a character then KbName returns the keycode for that character.

KbName('t')

If on the other hand you send in a number representing the keycode, then KbName will return the character of that keycode

KbName(84)
 While most keynames are shared between Windows and Macintosh, not all are. Some key names are used only on Windows, and other key names are used only on Macintosh. For a lists of key names common to both platforms and unique to each you can type:
edit KbName 
where they are listed.
 
 KbName will try to use a mostly shared (between Mac and Windows) name mapping if you add the command:
KbName('UnifyKeyNames'); 
 
at the top of your experiment script.
 
 

Practice Examples

The numbering of these practice examples matches their order in KbDemo, but I’m going through them in order of how complicated they are. That’s why the naming has a funny order (we start with KbPractice3).

This first demo just uses KbWait on its own. So we are waiting indefinately for the subject to press a key.

KbPractice3

 

% KbPractice3

% Wait for a key with KbWait.

% a simplified version of KbDemo part 3 by IF 4/2007

 

WaitSecs(0.5);

disp('Testing KbWait: hit any key.  Just once.');

ListenChar(2);

startSecs = GetSecs;

timeSecs = KbWait;

 

[keyIsDown, t, keyCode ] = KbCheck;

 

str=[KbName(keyCode), ' typed at time ', ...

    num2str(timeSecs - startSecs), ' seconds'];

disp(str);

ListenChar(0);  

 

KbWait returns the time in seconds (with high precision) since the computer started. Usually we don’t want to know the absolute time in seconds, but the time since some other event (e.g. since you put an image on the screen). So here we are calculating the time between the key press and an arbitrary start time.

KbPractice1

 

This uses KbCheck. So here we are constantly monitoring the state of the keyboard.

 

WaitSecs(0.5);

disp('1 of 4.  Testing KbCheck and KbName: press a key to see its number');

disp('Press the escape key to exit.');

KbName('UnifyKeyNames');

escapeKey = KbName('ESCAPE');

ListenChar(2);

while KbCheck; end % Wait until all keys are released.

% This is a loop that flips around doing anything as long as KbCheck

% reports back that a key is pressed on the keyboard. Means that the

% program doesn’t continue until all keys have been released.

 

while 1

    % while 1 is always true, so this loop will continue indefinitely.

    % Line 28 forceably breaks us out of the loop if the escape key is pressed.

   

    % Check the state of the keyboard.

    % See if a key is currently pressed on the keyboard. If not, we skip

    % the next for loop from lines 20-38, and basically check again almost

    % immediately.

   

    [ keyIsDown, seconds, keyCode ] = KbCheck;

   

    % If the user is pressing a key,

    % then display its code number and name.

    if keyIsDown

       

        % Note that we use find(keyCode) because keyCode is an array.

        str=['You pressed key ', num2str(find(keyCode)),' which is ', KbName(keyCode)];

       

        disp(str);

        % Display which key has been pressed.

       

        % If the key that has been pressed is the escape key break out of all loops

        % including the indefinite while loop.

        if keyCode(escapeKey)

            break;

        end

       

        while KbCheck; end

        % If the user holds down a key for more than a microsecond,

        % KbCheck will report multiple events, since computers are faster

        % than people's fingers

        % To condense multiple 'keyDown' events into a single event,

        % once a key has been pressed

        % we wait until all keys have been released

        % before going through the loop again

       

    end

end

ListenChar(0);  

 

KbPractice2

 

% KbPractice2

% Displays the number of seconds that have elapsed

% when the user presses a key.

% a simplified version of KbDemo by IF 4/2007

KbName('UnifyKeyNames');

WaitSecs(0.5);

disp('Testing KbCheck timing: please type a few keys.  (Try shift keys too.)');

disp('Type the escape key to exit.');

ListenChar(2);

escapeKey = KbName('ESCAPE');

startSecs = GetSecs;

 

while 1

    [ keyIsDown, timeSecs, keyCode ] = KbCheck;

    if keyIsDown

        str=[KbName(keyCode), ' typed at time ', ...

            num2str(timeSecs - startSecs), 'seconds'];

        disp(str)

       

% Because key presses made by humans are actually

% very slow compared to most programming commands you can actually

% put some interesting stuff inside the loop that is monitoring the

% keyboard without missing any key presses. But you can’t put too much

% in the loop without beginning to miss key presses. You can check this

% by making this loop more time consuming.

% How much stuff you will get away with will depend on the speed of your

% computer and the temporal characteristics of your keyboard.

 

%         for i=1:1

%             img=rand(100);

%         end

 

        if keyCode(escapeKey)

            break;

        end

 

        % If the user holds down a key,

        % KbCheck will report multiple events.

        % To condense multiple 'keyDown' events into a

        % single event, we wait until all

        % keys have been released.

        while KbCheck; end

    end

end

ListenChar(0);  

 

This example is very similar to that of KbPractice1 except here we report both what key was pressed, but also when it was pressed.

If you aren’t monitoring the keyboard at the very moment in time when the subject is pressing the key (e.g. because the program is busy displaying images) then the computer will not notice the key press. Because key presses made by humans are actually very slow compared to most programming commands you can actually put some interesting stuff inside the loop that is monitoring the keyboard without missing any key presses.

You can see how this works by adding the following commands between line 17-18 in Practice 2, e.g.

for i=1:1

      img=rand(100);

end

But you can’t put too much in the loop without beginning to miss key presses. You can check this by making this loop more time consuming

How much stuff you will get away with will depend on the speed of your computer and the temporal characteristics of your keyboard.

Summary

There are two ways to collect responses.

One (CharAvail/KbCheck) is to collect them from the event queue where they are stored. The advantage of that is you can do something interesting (display images) and then just pull the responses that the subject was making while you did that interesting thing from the event queue whenever you have time. The disadvantage is that once things have gone into the event queue there may be limited temporal accuracy about when they happened. The other disadvantage is that you can't stop the trial the sec that the subject gives a response.

The other way (KbCheck/KbWait) is to collect them directly from the keyboard. This has better temporal accuracy. But if you aren’t monitoring the keyboard at the very moment in time when the subject is pressing the key (e.g. because the program is busy displaying images) then the computer will not notice the key press. Because key presses made by humans are actually very slow compared to most programming commands you can actually put some interesting stuff inside the loop that is monitoring the keyboard without missing any key presses.

An example of a keypress function: WaitTill.m

Dealing with keypresses is something that naturally belongs in a function. Here's an example of a function designed to wait a specified amount of time for a keypress


function [keys,RT] = waitTill(waitTime,startTime)
 
% [keys,RT] = waitTill(waitTime,[startTime])
%
% Returns a vector of keys pressed and the timing of the presses during an
% interval of 'waitTime' seconds.  By default, the clock starts within the
% function, but if startTime is provided then the function will return
% waitTime seconds after startTime was defined with 'GetSecs'.  
%
% An empty variable is returned if no key was pressed during the interval.
%  
% 3/24/09 Written by G.M. Boynton at the University of Washington
 
%Initialize the output variables
keys = {};
RT = [];
 
%Read the clock if no clock time was provided
if ~exist('startTime')
    startTime = GetSecs;
end
 
%Give a warning if the waiting interval is zero or less
if GetSecs-startTime > waitTime
    disp('Warning! waitTill: waiting interval is less than zero')
end
 
%Turn off the output to the command window
ListenChar(2);
 
nKeys = 0;
%loop until waitTime seconds has passed since startTime was defined
while GetSecs-startTime < waitTime
    %see if a key is pressed
    [ keyIsDown, timeSecs, keyCode ] = KbCheck;
    if keyIsDown %a key is down: record the key and time pressed
        nKeys = nKeys+1;
        RT(nKeys) = timeSecs-startTime;
        keys{nKeys} = KbName(keyCode);
        %clear the keyboard buffer 
        while KbCheck; end
    end
end
%Turn on the output to the command window
ListenChar(0);