Adjust bar orientation through keyboard

URL of experiment: Not available yet.

Description of the problem:

Hi folks!
I want to design a task to run online. Basic idea is asking subjects to look at a gabor and adjust a bar, but I need codes in the CODE object to update my bar’s orientation every frame. The problem is, PsychoPy is not capable of translating codes in Py into JS, and I’m totally new to JS. So, can I just use KEYBOARD object to do the trick? Or do I have to write in JS to achieve that function? If the latter, is there any code available to do this? Thanks!

Start with just getting the task running locally, using a snippet of Python code if necessary. Once you are happy with it, then come back to us here.

The Python code should be very simple (no more than a couple of lines at most), and translating it to JavaScript should be straightforward. And that way, you have an experiment which can be very easily tested locally, while also being able to be tested online only as needed.

Thank you Michael, I have basically achieved this function, just with a small problem.

I can’t rotate the bar I created very fast using codes in “each frame”

This is my PsychoPy code for this, but when I created the same PsychoJS script, I can’t see the stimulus, I think it’s because I don’t have the “draw” and “flip”, can you help me?

while notFinished==True:
    win.winHandle.push_handlers(keyState)
    if keyState[key.LEFT]:
        Resp_bar.ori -= 1
    if keyState[key.RIGHT]:
        Resp_bar.ori += 1
    if keyState[key.RETURN] or keyState[key.SPACE]:
        BarOrientation = Resp_bar.ori
        notFinished = False
    if keyState[key.ESCAPE]:
        core.quit()
    Resp_bar.draw()
    Resp_point.draw()
    win.flip()
while continueRoutine {
  var thisResp = psychoJS.eventManager.getKeys();
  if ("left" == thisResp[0]) {
    BarOrientation = BarOrientation - 1;
  }
  if ("right" == thisResp[0]) {
    BarOrientation = BarOrientation + 1;
  }
  if ("return" == thisResp[0]) {
    continueRoutine = false;
  }
  // I think I need a "flip" here just like the above python code
}

Builder-generated code is based on a drawing loop event cycle: i.e. it aims to draw to the screen on every screen refresh, even if the content appears to be unchanging. Therefore, there are several things you shouldn’t do in code that lives in the “Every frame” tab, as that code must fit within a constraint of being completed within one screen refresh period (typically less than 16.7 ms on a 60 Hz display):

  • enter into an open-ended while loop: this will interrupt the drawing cycle, resulting in a long pause between successive win.flip() calls.
  • call win.flip() yourself: this will muck up the drawing cycle by calling win.flip() twice as often as expected, slowing things dramatically.

So in you code above, delete the while loops and remove win.flip().

All you need to do in this code is update the orientation value as required. Let Builder take care of the rest.

Thanks for the reply!

I had the python code (with flip and draw) in “begin routine”, nothing in “each frame”, and everything works fine, the bar rotates very naturally. But in the online exp (as I wrote in JS), the bar rotates so slow when I keep pressing on “left” or “right” and I can hear "beep"s while rotating, so I guess that’s the main problem.

calling win.flip() in “begin routine” seems fine, although theoretically I shouldn’t do that, I’ll try to modify that in the offline version, thanks for advice!

So, any idea of the online version?

Thanks in advance!!

It may appear natural, but you are entirely breaking the Builder-generated code (in effect, the routine never really starts, as you are highjacking the drawing). So don’t assume that anything that you have specified in Builder components will occur as scheduled.

It really would be better if you worked with Builder, rather than fighting against it, by putting the code in the “each frame” tab and letting Builder call win.flip() itself.

You’ll then have two quite different implementations, and shouldn’t trust that anything you test locally would apply in what runs online. Proceed at your own risk…

As stated, remove the while loop. That is blocking any other code from ever executing. And shift the code to “each frame” as suggested: trust Builder to take care of things for you, (instead of blocking its code, which will lead to all sorts of hard-to-predict issues).

Ahh sorry I made a mistake at the original post and I didn’t realize it. My JS code is in “each frame” and without while loop. The described problem occurred, any idea?

var thisResp = psychoJS.eventManager.getKeys();
if ("left" == thisResp[0]) {
  BarOrientation = BarOrientation - 1;
}
if ("right" == thisResp[0]) {
  BarOrientation = BarOrientation + 1;
}
if ("return" == thisResp[0]) {
  continueRoutine = false;
}

https://pavlovia.org/run/mauro83/test/html/
This is the exp.

I’ll certainly modify my offline version, thanks again.

The code as posted above wouldn’t influence the visibility of the the stimulus. This is the advantage of using a single Builder file for running both locally and online, with the only difference being in the code components. That way you can isolate any issues to a code component. In this case, you’ll need to look elsewhere for the issue.

Sorry, I am not being clear. This is the python code (in begin routine) that I usually use to obtain a continuous bar adjustment on a local experiment.
Is there a more elegant way to have the same result (continuous bar movement) without breaking the Builder code?
More importantly, is there a way to translate this python code into JS for an online bar adjustment experiment?

BarOrientation = random.randint(-90, 89) # Random bar orientation
Resp_bar.ori = BarOrientation
Resp_bar.draw()
keyState=key.KeyStateHandler()
notFinished=True

while notFinished==True:
    win.winHandle.push_handlers(keyState)
    if keyState[key.LEFT]:
        Resp_bar.ori -= 1
    if keyState[key.RIGHT]:
        Resp_bar.ori += 1
    Resp_bar.draw()
    win.flip()

As stated above, put the real-time update code into the “each frame tab”, and remove the while, the .draw(), and the win.flip(). Basically exactly as you have for your javascript.

Once you have the Python solution working, you’ll know what to do for the JavaScript approach.

Thanks for that! I’ve solve this problem. Putting those detecting in “each frame tab” seems imperfect because it would cause some detection miss, and makes the rotating not smooth enough.

My solution to this, adding an eventlistener at “begin routine tab”, and use addInterval to make it check for keydown frequently, this causes no conflict and makes rotating smooth.

I appreciate you help a lot! Thanks Michael!

one more trivial question I asked in another post, is there anyway to go back to previous routine in online experiment?

What’s the equivalent function in JS I can call to remove a loop? Below is your answer to this question: How to come back to previous routine using code?

This is usually done with loops. Have a loop around both routines. At the end of the second, if you want to repeat the first, do nothing, it will run again automatically. If you don’t want to go back, then set your_loop_name.finished = True

Dear zycyc,
could you share your code for this solution (the eventlistener + addinterval +keydown approach you mentioned)? I am struggling with a similar problem and this would be interesting to see!

Thanks
Franka