Display contingent feedback

Hello @Michael ! I have a similar question.

v2020.1.3
Windows10

In my experiment, participants need to give responses to image stimuli with two response keys (i.e., A and L). I also want to show error feedback (a red X) when the resp.corr==0, and continue to the next trial when resp.corr==1.

However (the trşcky part for me), if they do not give a response within 3 seconds, I want to show feedback (i.e., please respond), and then they need to press space to continue to the experiment. The reason that I am doing the latter because I will run my study online, and I don’t want participants to do something else while the experiment is running background.
I attempted to write the code. However, I couldn’t manage to do it.

Begin Experiment                           
msg= ''
Begin Routine 
end_at_2s = False
iif t >= 2.0 and end_at_2s: 
    continueRoutine = False 
    msg='Please respond'
   keys = event.getKeys()

if resp.corr==0:
    msg='X'

else:
    continue 

I know that I need to assign ‘space’ key after msg='Please respond' however I couldn’t manage to do it.
I also put ‘space’ into the allowed keys section

Thanks in advance!
Emre

Hello, you can avoid a bit of code here. Just set all the stimuli and the keyboard component in the first routine to end at 3 s, and the keyboard component set to “Force end of routine”. That way, a response will end the routine immediately, or else it will time out after three seconds.

Then you just need to control what will happen in the next routine, which displays the feedback and waits for the space bar:

  1. Display 'Please respond" if it timed out.
  2. Display ‘X’ if the response was incorrect.
  3. Skip the routine entirely if the response was correct (i.e. just go on to the next trial immediately).

So in the first routine’s “Begin routine” tab:

msg = 'Please respond' # default behaviour

In its “End routine” tab:

if not resp.corr:
    msg = 'X'

And then in the “begin routine” tab of the feedback routine:

if resp.corr:
    continueRoutine = False

The feedback routine will have a text stimulus (set to show $msg) and a keyboard component for the subject to push “space”, so you don’t need to handle that in code.

Thank you so much for the fast reply @Michael!

I got the idea entirely. However, I may be putting ‘msg’ at the wrong places. Because I have attempted to run the experiment offline on my laptop, and I couldn’t manage what you described.

Correct responses skip the routine entirely without a problem.
Incorrect responses show an X and then wait for me to respond with space (Normally I would like to continue routine without space press requirement if the response was incorrect)
No responses do not display the ‘please respond’ message after 3 seconds without a response. I just get a rex X as there was a wrong keypress, and then I need to press space to continue to the next trial.

I have put one code component for the first routine and another for the feedback routine (I guess I am making a mistake here)

Below there is the first routine: All texts and keyboard set to 3 seconds. The code had

//Begin Routine 
msg='Please respond'
//End Routine 
if not respp.corr:
   msg='X'

Below there is a feedback routine. The text component is set to display $msg, and the keyboard component requires 'space' keypress. And code component had

//Begin Routine
if respp.corr:
    continueRoutine = False

Probably I placed something in the wrong place, but I could not figure out what I did wrong.

Many Thanks,
Emre

OK, there was a problem in my code logic, because a non-response also leads to resp.corr being False.

So delete the “begin routine” code setting msg = 'Please respond', and alter the “end routine” code to be something like:

if not resp.corr: # there was not a correct response
    if not resp.keys(): # i.e. the list is empty
        msg = 'Please respond'
    else: # they responded but incorrectly
        msg = 'X'

I can’t remember if resp.keys() actually needs the (). Try it and see.

If you don’t want the subject to press a key on the feedback routine, just delete the keyboard component, and give the text component a fixed duration so the routine will end automatically.

If you do want the subject to press space when the feedback is 'Please respond', let me know.

@Michael
feedback

Currently, I have a code component in practice_word and it contains only following in the end routine. (I needed to add if respp.corr: /n msg=''
because all responses were classified as either no response or mistake.

//End Routine
if respp.corr:
    msg=''
elif not respp.corr:
    if not respp.keys:
        msg='Please Respond'
    else:
        msg='X'

feedback_word has text component set to show $msg and I there is no keyboardd component that requires ‘space’

And importantly I want the subjects to press space when the feedback is 'Please respond'

Thank you!!
Emre

@Michael, I worked a bit more on my code.
I needed to update my code following. (Normally, I would use continue if respp.corr is correct in Python; however, it seems like in Javascript continue does not work. Therefore, I set the duration of msg=’’ to 0 in Python that creates no error in JS. I hope I did it correctly!

if respp.corr:
    msg=''
    msgDuration=0
elif not respp.corr:
    if not respp.keys:
        msg='Please Respond'
        msgDuration=1
    else:
        msg='X'
        msgDuration=1

Hello @Michael !
Sorry for the repetitive posts about the coding! However, I had some free time and I searched more about this feedback style that I needed.

I suppose that I need to write down my code into each frame instead of end routine

therefore I did the following;
ert

In trial I added one code consisted of;

//Each Frame

keys = event.getKeys()

if keys:
    if 't' in keys and resp.keys==corrAns:
        msg=''
        msgTime=0
    elif 't' in keys and resp.keys!=corrAns:
        msg='X'
        msgTime=1
    elif 'v' in keys and resp.keys==corrAns:
        msg=''
        msgTime=0
    elif 'v' in keys and resp.keys!=corrAns:
        msg='X'
        msgTime=1
elif not resp.keys:
    msg='Please respond'
    msgTime=10

also keyboard component is set to force end the routine

and feedback has one text component set to show $msg

Now my aim is to present the ‘Please respond’ message for an unlimited time if participants do not respond in 3 seconds. And in order for participants to continue the task, they need to press ‘space’. However, I couldn’t manage to add the space button press.

Many thanks for the help,

Emre

OK, in the feedback routine, the “start routine” tab should be something like:

# don't run this routine at all if they gave a correct response:
if respp.corr:
    continueRoutine = False

then in the “each frame” tab, you need to handle the other two conditions:

keys = event.getkeys()

# if the response was wrong, then end if a key is 
# pushed, or regardless at 3 s: 
if msg == 'X':
    if keys or t >= 3.0:
        continueRoutine = False
else: # was no response, so wait indefinitely until a key is pushed
    if keys:
        continueRoutine = False

Note that you are handling the keypresses on this routine entirely in code, so no keyboard component is necessary, and the text component should have no set duration (so it will last indefinitely, until this code ends the routine).

@Michael Thank you so much for the help! I managed to do it on builder!!

However, I have problem to run it online, I suppose event.getkeys() is not a function in JS, therefore, I need to use something fits to JS like psychoJS.eventManager.getKeys() however I couldn’t manage to write the Java Code.

For example in practice_word following is the code;

keys = event.getKeys()
if keys:
    if 't' in keys and keys == corrAns:
        continueRoutine=False 
    elif 't' in keys and keys != corrAns:
        msg='X'
    elif 'v' in keys and keys == corrAns:
        continueRoutine=False 
    elif 'v' in keys and keys != corrAns:
        msg='X'
elif not keys:
        msg='You need to give faster responses. Please press space to continue'

I get following JS code when I use Auto -> JS;

var _pj;
function _pj_snippets(container) {
    function in_es6(left, right) {
        if (((right instanceof Array) || ((typeof right) === "string"))) {
            return (right.indexOf(left) > (- 1));
        } else {
            if (((right instanceof Map) || (right instanceof Set) || (right instanceof WeakMap) || (right instanceof WeakSet))) {
                return right.has(left);
            } else {
                return (left in right);
            }
        }
    }
    container["in_es6"] = in_es6;
    return container;
}
_pj = {};
_pj_snippets(_pj);
keys = event.getKeys();
if (keys) {
    if ((_pj.in_es6("t", keys) && (keys === corrAns))) {
        continueRoutine = false;
    } else {
        if ((_pj.in_es6("t", keys) && (keys !== corrAns))) {
            msg = "X";
        } else {
            if ((_pj.in_es6("v", keys) && (keys === corrAns))) {
                continueRoutine = false;
            } else {
                if ((_pj.in_es6("v", keys) && (keys !== corrAns))) {
                    msg = "X";
                }
            }
        }
    }
} else {
    if ((! keys)) {
        msg = "You need to give faster responses. Please press space to continue";
    }
}

Can I simply change the event.getKeys() function into some other function to solve this problem?

I really appreciate all the help
Thank you so much,
Emre

Yes.

Although I don’t know JavaScript we’ll enough to know if every thing else is correct though.

If you define event=psychoJS.eventManager; in code_JS, a JavaScript code block at the start of your experiment, then you can use event.getKeys() as normal in your code.

Best wishes,

Wakefield

@Michael Thank you so much for the help and advice!

@wakecarter the document that you provided is amazingly informative! Thank you so much.
I edited the code a little bit already by using it (i.e., I moved the continueRoutine = False code from Begin Routine to Each Frame).
With defining event in code_JS helped the experiment work online however, I have one following question.
I want to;

  1. skip next trial when the correct response is given.
  2. give an ‘X’ feedback when the incorrect response is given and skip next trial after 1 second.
  3. if there is no response at all. ‘Please respond’ message needs to be presented and participants need to press space in order to continue the experiment.

For now, I managed to do first continuing when the response is correct but 2nd and 3rd steps I couldn’t.

When I respond correctly it jumps to the next trial without a problem. However, when I respond incorrectly the trial (i.e., word) disappears but I dont get the feedback message ‘X’ and I need to press ‘space’ to continue (which I want to skip to the next trial after 1 second). Also, If I don’t respond after 2 second trial disappears without displaying ‘please respond message’ and requires space key press (which I want in this case).
I will share the PY and JS code. I would be really grateful if you can help me to realize where I did a mistake to I need to edit.

 #There are two routines. First is called trial and the other one is feedback. This is the Py code (in Each Frame) in trial 
keys = event.getKeys()

if keys:
    if 't' in keys and keys == corrAns:
        continueRoutine=False 
    elif 't' in keys and keys != corrAns:
        msg='X'
    elif 'v' in keys and keys == corrAns:
        continueRoutine=False 
    elif 'v' in keys and keys != corrAns:
        msg='X'
elif not keys:
        msg='You need to give faster responses. Please press space to continue'
 //JS code in Each Frame in trial
var _pj;
var msg;
var keys;
event=psychoJS.eventManager
function _pj_snippets(container) {
    function in_es6(left, right) {
        if (((right instanceof Array) || ((typeof right) === "string"))) {
            return (right.indexOf(left) > (- 1));
        } else {
            if (((right instanceof Map) || (right instanceof Set) || (right instanceof WeakMap) || (right instanceof WeakSet))) {
                return right.has(left);
            } else {
                return (left in right);
            }
        }
    }
    container["in_es6"] = in_es6;
    return container;
}
_pj = {};
_pj_snippets(_pj);
keys = event.getKeys();
if (keys) {
    if ((_pj.in_es6("t", keys) && (keys === corrAns))) {
        continueRoutine = false;
    } else {
        if ((_pj.in_es6("t", keys) && (keys !== corrAns))) {
            msg = "X";
        } else {
            if ((_pj.in_es6("v", keys) && (keys === corrAns))) {
                continueRoutine = false;
            } else {
                if ((_pj.in_es6("v", keys) && (keys !== corrAns))) {
                    msg = "X";
                }
            }
        }
    }
} else {
    if ((! keys)) {
        msg = "You need to give faster responses. Please press space to continue";
    }
}

#Py code (in Each Frame) in feedback routine 
keys = event.getKeys()
if resp.corr:
    continueRoutine = False 
# if the response was wrong, then end if a key is 
# pushed, or regardless at 3 s: 
elif msg == 'X':
    if t >= 1.0:
        continueRoutine = False
else: # was no response, so wait indefinitely until a key is pushed
    if keys and 'space' in keys: 
        continueRoutine = False
// JS code in Feedback routine
var _pj;
var msg;
var keys;
function _pj_snippets(container) {
    function in_es6(left, right) {
        if (((right instanceof Array) || ((typeof right) === "string"))) {
            return (right.indexOf(left) > (- 1));
        } else {
            if (((right instanceof Map) || (right instanceof Set) || (right instanceof WeakMap) || (right instanceof WeakSet))) {
                return right.has(left);
            } else {
                return (left in right);
            }
        }
    }
    container["in_es6"] = in_es6;
    return container;
}
_pj = {};
_pj_snippets(_pj);
keys = event.getKeys();
if (resp.corr) {
    continueRoutine = false;
} else {
    if ((msg === "X")) {
        if ((t >= 1.0)) {
            continueRoutine = false;
        }
    } else {
        if ((keys && _pj.in_es6("space", keys))) {
            continueRoutine = false;
        }
    }
}

I know that the message has been quiet long, excuse me for this.
Thank you in advance!
Emre

Off the top of my head, I don’t think keys==correctAns is going to work. One is an array and the other is a string.

How about

keys = event.getKeys()

if keys:
    if 't' in keys and keys[0] == corrAns:
        continueRoutine=False 
    elif 't' in keys:
        msg='X'
    elif 'v' in keys and keys[0] == corrAns:
        continueRoutine=False 
    elif 'v' in keys:
        msg='X'
else:
        msg='You need to give faster responses. Please press space to continue'

@wakecarter thank you for suggestions!
Sorry for the late response. I coudn’t have chance to reply earlier.
I try the code that you provided. It works without a problem offline. However, I have the same problems with online experiment.

keys = event.getKeys()
if keys:
    if 't' in keys and keys[0] == corrAns:
        continueRoutine=False 
    elif 't' in keys:
        msg='X'
    elif 'v' in keys and keys[0] == corrAns:
        continueRoutine=False 
    elif 'v' in keys:
        msg='X'
else:
        msg='You need to give faster responses. Please press space to continue'```


I get the following code by Auto → JS and added the first three lines manually.

var msg;
var keys;
event=psychoJS.eventManager //Until here I added manually. 

var _pj;
function _pj_snippets(container) {
    function in_es6(left, right) {
        if (((right instanceof Array) || ((typeof right) === "string"))) {
            return (right.indexOf(left) > (- 1));
        } else {
            if (((right instanceof Map) || (right instanceof Set) || (right instanceof WeakMap) || (right instanceof WeakSet))) {
                return right.has(left);
            } else {
                return (left in right);
            }
        }
    }
    container["in_es6"] = in_es6;
    return container;
}
_pj = {};
_pj_snippets(_pj);
keys = event.getKeys();
if (keys) {
    if ((_pj.in_es6("t", keys) && (keys[0] === corrAns))) {
        continueRoutine = false;
    } else {
        if (_pj.in_es6("t", keys)) {
            msg = "X";
        } else {
            if ((_pj.in_es6("v", keys) && (keys[0] === corrAns))) {
                continueRoutine = false;
            } else {
                if (_pj.in_es6("v", keys)) {
                    msg = "X";
                }
            }
        }
    }
} else {
    msg = "You need to give faster responses. Please press space to continue";
}

I still have the problems that I had earlier;

Thank you so much!
Best wishes,
Emre

Hello @wakecarter,

I have a question regarding the PsychoPy Python to Javascript document
that you provided earlier. Can I share this document that you wrote with colleagues in my lab group?

Best regards,
Emre

Absolutely!

I’d like to see it shared as widely as possible.

Best wishes,

Wakefield

Hi @wakecarter.

I have a small question about the screen scale.
I would like to use the screen scale method not as the first routine. First I want to give some general information about the online study etc. Since all of the content will be text and I will be using the “norm” unit I do not need x_scale or y_scale. However, when I set the screen scale routine as the second or third routine in my flow, the experiment runs without the screen scale routine. No error or no crash simply I cannot see the resizeable credit size and experiment runs without a problem. Of course, since my screen is not calibrated my images are presented pretty bad. Do I need to do any adjustments to use your screen scale method later in my flow?

Many Thanks,
Emre

Hi.

Is the previous routine ended with a space bar?

One option would be to change

    if 'space' in keys:
        continueRoutine=False

into

    if 'space' in keys and t > 1:
        continueRoutine=False

I would also add event.clearEvents() to the Begin Routine tab.