About timeouts

PennController for IBEX Forums Support About timeouts

Viewing 11 posts - 1 through 11 (of 11 total)
  • Author
    Posts
  • #10797
    Larissa_Cury
    Participant

    Hello! I’m wondering why the code below is indeed working. It is working, but I’m not sure WHY it is working. I’d like to know if the code is doing what I think it is. I’ve put my guesses into comments. I have a trial WITH feedback (a) and a trial WITHOUT feedback + Reaction time (b)

    I’m mostly in doubt about the .wait() command

    TRIAL A) WITH FEEDBACK

    
    Template("simonStimuliPracticeNeutral.csv", row =>
      newTrial("practice--neutral--block",
      ...
      ,
      newTimer("timeout",5000).start() // HERE THE TIMEOUT STARTS. USER HAS ONLY 5s to ANSWER
      ,
      newImage('stimuli', row.direction).print()  // Print arrow left or right
      // hiding css details 
    })
    ,
    newKey("answerKey","SK")
            .log("all")
            .callback( getTimer("timeout").stop() ) // THE KEYPRESS STOPS THE TIME, RIGHT? 
    ,
    getTimer("timeout").wait() // IF THERE ISN'T A KEY PRESS, THEN THE TIME ISN'T STOPPED, RIGHT? BUT how is it actually functioning? How is the 'wait()' functioning here?
    ,
    getImage('stimuli').remove()  // remove the image so that the feedback answer appears
    ,
    getKey("answerKey") // Check for the correct key press or timeout
        .test.pressed(row.correctKey) // check keys (options are K and S)
        .success(      
            newText("Correct ✅").center().print(), // Display "Correct" message 
            newTimer('wait-positive-feedback', 500).start().wait() // Wait for positive feedback timer
        )
        .failure(
             getKey("answerKey").disable().test.pressed("K", "S")  // IF NEITHER K OR S was pressed, then display the timeout
            .failure(
                // Display "Timeout" message and wait for feedback timer
                newText("Timeout ⏰").center().print(),
                newTimer('wait-feedback', 500).start().wait()
            )
            .success(         // IF EITHER K OR S was pressed, then display the INCORRECT ANSWER IF IT DOENSN'T MATCH THE row.correctKey key
                newText("Incorrect ❌").center().print(),  // display negative feedback
                newTimer('wait-neg-feedback', 500).start().wait()   // Wait for negative feedback timer
            )
        )
     )
    );
    

    TRIAL B) WITHOUT FEEDBACK + RT, ONLY THE TIMEOUT

    
    Template("simonStimuliNeutral.csv", row =>
      newTrial("neutral--block",
    ...
      ,
      newText('fixationCross', '+').center().print().cssContainer({'font-size': '200px'})
      ,
      newTimer('waitFixation', row.fixationTime).start().wait()
      ,
      getText('fixationCross').remove()
      ,
      newTimer("timeout",5000).start() // User has 5s to answer; otherwise => timeout
      ,
      newImage('stimuli', row.direction).print()  // Print arrow left or right
    // CSS HIDDEN //
    })
    ,
    newVar("RT").global().set( v => Date.now() ) // compute Reaction Time
    ,
    newKey("answerKey","SK")
            .log("all")
            .callback( getTimer("timeout").stop() ) // THE KEYPRESS STOPS THE TIME, RIGHT?  
    ,
    getTimer("timeout").wait() // wait for the timeout to finish (no keypress, the time isn't stoped) ???
    ,
    getVar("RT").set( v => Date.now() - v )  // Set Reaction Time value BEFORE the msg for the timeout so that I 
    //compute the timeout in case the user doens't press a key and his/her RT if she/he presses. 
    //BUT: WHY is it working and NOT computing the RT + 5000s of the timeout 
    //if I'm computing the RT AFTER the .wait() ?
    ,
    getImage('stimuli').remove()
    ,
    getKey("answerKey").disable().test.pressed().failure(
                 newText("Incorrect", "Consegue ir mais rápido? ⏰").center().print(),
                 newTimer('wait-feedback',500).start().wait()
             )
    )
     // LOG A BUNCH OF STUFF
    );
    
    • This topic was modified 9 months, 1 week ago by Larissa_Cury. Reason: edit coding
    #10799
    Jeremy
    Keymaster

    Hi,

    callback commands inform the script of what to do whenever the relevant event happens, and immediately move on to the next line in the script. By contrast, wait commands halt the execution of the script until the relevant event happens, after which only do they move on to the next line in the script. When you have these lines in a script:

    newTimer("timeout", 5000).start() // A
    ,
    newVar("RT").global().set( v => Date.now() ) // B
    ,
    newKey("answerKey","SK").log("all").callback( getTimer("timeout").stop() ) // C
    ,
    getTimer("timeout").wait() // D
    ,
    getVar("RT").set( v => Date.now() - v ) // E

    The script first executes (A): it starts a 5s timer and moves on to (B) without waiting for the timer to elapse (because there is no wait command in A). At (B), which is executed immediately after (A), the scripts sets the Var element to the current timestamp, ie. a timestamp that corresponds to when the timer was started. The script immediately moves on to (C), which evaluates the callback command: here, the script learns that whenever the participant presses S or K, the command getTimer("timeout").stop() should take effect. But importantly, there is no wait command in (C) so the script immediately moves on to (D), but it now keeps in mind what should happen were the participant to press S or K at any point. At (D) the script sees a wait command so it stays on (D) until the timer has stopped. This could happen because the 5s have elapsed, the normal course of events for a timer; or it could happen because the timer was stopped early, which is precisely what we instructed the script to do if the participant ever presses S or K, in the preceding callback command. So when the timer stops, regardless of which scenario we’re in, the script then moves on to (E) and sets the Var element to the current timestamp minus the previous one. If we’re in a scenario where the participant didn’t press S or K, then the script must have reached this line after waiting 5s, so the subtraction will result in that duration. But if we’re in a scenario where the timer was stopped because the user pressed S or K, then it hasn’t been 5s yet, and the subtraction will reflect that, and the result of the subtraction will be a lesser value. Once the Var element has been set, the script immediately moves on to the next line (if any)

    Jeremy

    #10800
    Larissa_Cury
    Participant

    Hi,Jeremy! Thank you for this detailed answer, I REALLY appreciate it! Now I got it! So, I can say that the .stop() command makes the .wait() understand that the time has finished without waiting for the whol 5s, right? Now I got it! What about the feedback in the previous trial:

    It should be: “If user presses the correctKey, show the ✅; if he/she presses the wrong key, ❌; in case of a timwout (5 seconds), show the timeout message ⏰

    Is this the correct syntax? It seems to be working just fine, but I’m not 100% sure

    
    getKey("answerKey") // Check for the correct key press or timeout
        .test.pressed(row.correctKey) // check keys (options are K and S)
        .success(      
            newText("Correct ✅").center().print(), // Display "Correct" message 
            newTimer('wait-positive-feedback', 500).start().wait() // Wait for positive feedback timer
        )
        .failure(
             getKey("answerKey").disable().test.pressed("K", "S")  // IF NEITHER K OR S was pressed, then display the timeout
            .failure(
                // Display "Timeout" message and wait for feedback timer
                newText("Timeout ⏰").center().print(),
                newTimer('wait-feedback', 500).start().wait()
            )
            .success(         // IF EITHER K OR S was pressed, then display the INCORRECT ANSWER IF IT DOENSN'T MATCH THE row.correctKey key
                newText("Incorrect ❌").center().print(),  // display negative feedback
                newTimer('wait-neg-feedback', 500).start().wait()   // Wait for negative feedback timer
            )
        )
     )
    
    #10801
    Jeremy
    Keymaster

    You want to replace getKey("answerKey").disable().test.pressed("K", "S") with getKey("answerKey").disable().test.pressed(): this line is in a failure command which already corresponds to a situation where the correct key wasn’t pressed so if, in that situation, test.pressed() succeeds it means that the incorrect key was pressed, and if it fails it means that no key was pressed

    Other than that I don’t see any problems with this piece of code

    Jeremy

    #10802
    Larissa_Cury
    Participant

    Thanks for the quick response!! So, the logic is:

    .success: ✅ : if the eow.correctKey was pressed
    timeout ⏰: if NO KEY was pressed
    error message ❌: if a key != from row.correctKey (already evaluated before) was pressed. Right?

    
    getKey("answerKey") // Check for the correct key press or timeout
        .test.pressed(row.correctKey) // check keys (options are K and S)
        .success(      
            newText("Correct ✅").center().print(), // Display "Correct" message 
            newTimer('wait-positive-feedback', 500).start().wait() // Wait for positive feedback timer
        )
        .failure(
             getKey("answerKey").disable().test.pressed()  // => IF ANY KEY WAS PRESSED Was pressed, then display the timeout
            .failure(
                // Display "Timeout" message and wait for feedback timer
                newText("Timeout ⏰").center().print(),
                newTimer('wait-feedback', 500).start().wait()
            )
            .success(         // IF A KEY != FROM row.correctKey was pressed, then display the INCORRECT
                newText("Incorrect ❌").center().print(),  // display negative feedback
                newTimer('wait-neg-feedback', 500).start().wait()   // Wait for negative feedback timer
            )
        )
     )
    
    #10803
    Larissa_Cury
    Participant

    @Jeremy, If I include an audio effect, will I bias the reaction time and the time out ? Like this:

    does it influence the rt and the timeout?

    
    ...
    newAudio('keyPressAudio', "keyPress1.mp3")
    ,
    newKey("answerKey","SK")
            .log("all")
            .callback( getTimer("timeout").stop(), // the key stops the time
                       getAudio('keyPressAudio').play())   ////////////////// <--------------------- HERE 
    ,
    getTimer("timeout").wait() // wait for the timeout to finish (no keypress, the time isn't stoped) ???
    ,
    getImage('stimuli').remove()
    ,
    // Check for the correct key press or timeout
    getKey("answerKey")
        .test.pressed(row.correctKey) // check keys
        .success(
            // Display "Correct" message and wait for positive feedback timer
            newText("Correct ✅").center().print(),
            newTimer('wait-positive-feedback', 500).start().wait()
        )
        .failure(
            // Check for timeout before displaying "Incorrect" message
            getKey("answerKey").disable().test.pressed()
            .failure(
                // Display "Timeout" message and wait for feedback timer
                getVar('errorCount').set(v=> v+1),
                newText("Timeout ⏰").center().print(),
                newTimer('wait-feedback', 500).start().wait()
            )
            .success(
                // Display "Incorrect" message and wait for negative feedback timer
                getVar('errorCount').set(v=> v+1),
                newText("Incorrect ❌").center().print(),
                newTimer('wait-neg-feedback', 500).start().wait()
            )
        )
    
    #10804
    Jeremy
    Keymaster

    It won’t make a difference, since the wait command will be released as soon as getTimer("timeout").stop() from the callback command has been executed; the command getAudio('keyPressAudio').play() inside callback will just run in parallel to getImage('stimuli').remove() and the commands coming after that (including the set commands further down) from the main thread

    Jeremy

    #10805
    Larissa_Cury
    Participant

    Perfect! Thank you very much (again) for the detailed feedback 🙂

    #10809
    Larissa_Cury
    Participant

    Hi, @Jeremy ! What is the best way to get the same effect (play the click sound when a button is clicked) using a selector? Like this:

    I tried to use a .callback as well, but it didn’t work…With the code below, sometimes it works, sometimes it doesn’t (the audio is just an effect of a click)

    
    newImage("mainImage", row.image)
      .css({
      "display": "block",
      "position": "fixed",
      "top": "35vh", // 50% of the viewport height
      "left": '50%', // Use 50% for horizontal centering and 15% and 85% left/right
      "transform": "translate(-50%, -50%)",
     //  "width": "25em", // Replace with the desired width of the image in em units
     // "height": "25em", // Replace with the desired height of the image in em units
      "z-index": "9999", // To make sure the image is on top of other elements
    })
    .print()
    ,
    newAudio('keyPressAudio', "keyPress1.mp3")
    ,
     newSelector("position")
    ,
      newCanvas("buttons",1180,100)
      .add("center at 10%", "bottom at 100%", newButton("btn--1", "1").selector("position"))
      .add("center at 30%", "bottom at 100%", newButton("btn--2", "2").selector("position"))
      .add("center at 50%", "bottom at 100%", newButton("btn--3", "3").selector("position"))
      .add("center at 70%", "bottom at 100%", newButton("btn--4", "4").selector("position"))
      .add("center at 90%", "bottom at 100%", newButton("btn--5", "5").selector("position"))
      .css({
          "top": "40vh"
      })
      .center()
      .print()
        ,
        getSelector("position")
          .once()
          .wait()
          .log()
        ,
        getAudio('keyPressAudio').play()
    )
     // .log("item", row.imageAudio)
      .log('image', row.image)
      .log("correctKey", row.correctKey)
    );
    
    #10810
    Larissa_Cury
    Participant

    getAudio('keyPressAudio').play().wait('first') <- this seems to work, but I don’t think that’s the best way to do it, right?

    #10812
    Jeremy
    Keymaster

    Hi Larissa,

    If you end the trial immediately after a selection is made, there just won’t be enough time to hear the audio which starts playing but is stopped immediately, along with the trial’s end. Using wait on the Audio element, as in your last message, after the wait on the Selector element has been released, is one solution to make sure that the trial doesn’t end before the audio is done playing

    Jeremy

Viewing 11 posts - 1 through 11 (of 11 total)
  • You must be logged in to reply to this topic.