Jeremy

Forum Replies Created

Viewing 15 posts - 1,276 through 1,290 (of 1,522 total)
  • Author
    Posts
  • Jeremy
    Keymaster

    Hi Nickolas,

    You won’t be able to specify a different filename for the results file as all the results are sent to the same address, and storing them in the results file is handled on the server side entirely.

    However it should be simple enough to look at the lines from your results files that correspond to your last trial so as to list all the participants who made it to the end of your experiment, and then filter the results using that list.

    Jeremy

    in reply to: Behavioural task score #5446
    Jeremy
    Keymaster

    Hi Zoë,

    You Text element does get printed onto the page, but notice that you’re also ending the Timer element (early) at the same time, and there’s nothing left to do in your trial once the Timer has ended, so your script immediately goes to the next trial. Maybe this would make more sense:

    Template("recall_practice_stimuli.csv",
        row => 
        newTrial("recall-"+row.block,
        newVar("score", 0).global(), 
        newText(row.item)
            .center()
            .css("font-size", "2em")
    	.css("font-family", "verdana")
            .print()
        ,
        newText("instruction", "In which condition did you encounter this word in the experiment? Press the F KEY for INNER, the J KEY for OUTER, and the SPACEBAR for NEW WORD")
            .print()
        ,
        newTimer("window", 5000)
            .start()
        ,
        newKey("question1", "FJ ")
            .log()
            .callback(
                getText(row.item).remove()
                ,
                getText("instruction").remove()
                ,
                getTimer("window").stop()
            )
        ,
        getTimer("window")
            .wait()
        ,
        getKey("question1")
            .test.pressed("F")
            .success(
                newText("Good job!").print()
                ,
                newTimer(1000).start().wait()
            )
        )
        .log(row.item)
        .log(row.Group)
        .log(row.condition)
        .log(row.correctKey)
    );

    This way you leave 1s for your participant to read the “Good job!” feedback

    Jeremy

    in reply to: DashedSentence in a PennController trial #5441
    Jeremy
    Keymaster

    I don’t know that it’s too much effort, it’s more that there are decisions to be made. Using the longest word length is just the foolproof method, as it makes sure that all the blanks will definitely have the same width. You can try using a shorter constant width, but the question becomes “what to do with longer words?”

    No matter how you fine-tune this parameter though, as you say you won’t have control over the page resolution when your participants take your experiment, so there’s no way to guarantee 100% that no wrap will be introduced.

    Jeremy

    in reply to: DashedSentence in a PennController trial #5439
    Jeremy
    Keymaster

    Hello Max,

    The special characters in forums posts mess with some characters in javascript code, it’s really frustrating I must say. I tried editing your last message so it displays the javascript code correctly.

    In order to make all words appear the same length when masked, would you be willing to use the longest-word’s length for all of them? Because that would make life easier:

    showWord = (s,i,m) => '<p>'+s.map( (w,n) =>
                `<span style='display: inline-block; text-align: center; width: ${m}ch; 
                ${(i===n?'':'border-bottom:solid 1px black;\'><span style=\'visibility:hidden;\'')}'>
                ${w}${(i===n?"":'</span>')}</span>`
            ).join(' ')+'</p>'
            
    dashed = (name, sentence) => {
        let words = sentence.split(' '), maxwidth = 0;
        words.map(w=>maxwidth=Math.max(w.length,maxwidth));
        return [
            [newText(name, showWord(words,null,maxwidth)).print()], 
            ...words.map( (w,i) => [newKey(`${name}-${i}-${w}`," ").log().wait() , getText(name).text(showWord(words,i,maxwidth))] ),
            [newKey(`${name}-last`," ").log().wait()]
        ].flat(1);
    }
    
    newTrial(
        ...dashed("test","A local man is in a great amount of debt due to gambling.")
        ,
        newButton("Finish").print().wait()
    )

    Jeremy

    in reply to: Track Mouse Position #5438
    Jeremy
    Keymaster

    Hi Irene,

    You will need a slightly different approach. Here is what I suggest: whenever there is a click (as captured by the Selector’s callback command) you append to two other Var elements the current values of the mousex/mousey Var elements (which are reset whenever the mouse moves, thanks to the MouseTracker’s callback command). Then you log the values of those two Var elements as a string of coordinates separated by periods (or any character for the matter, except a comma which is already used to separate fields in the CSV results file).

    newTrial(
        newVar("mousex"),newVar("mousey"),
        newVar("clickx",[]),newVar("clicky",[])
        ,
        newMouseTracker("mouse")
            .callback( (x,y) => [getVar("mousex").set(x)._runPromises(),getVar("mousey").set(y)._runPromises()])
            .start()
        ,
        newCanvas("screen", "90vw","90vh").print("left at 0","top at 0"),
        newSelector().add(getCanvas("screen")).callback(
            getVar("clickx").set(v=>[...v,getVar("mousex").value]),
            getVar("clicky").set(v=>[...v,getVar("mousey").value])
        )
        ,
        newButton('go!')
            .size("10vw", "10vh")
            .print("left at 90vw","top at 90vh")
            .wait()
        ,
        getMouseTracker("mouse").stop()
        ,
        getVar("clickx").log().set(v=>v.join('.')),
        getVar("clicky").log().set(v=>v.join('.'))
    )

    Here’s an example of the two result lines that you get (I clicked three times, hence the three period-separated values):

    1590592091,MD5HASHEDIP,PennController,0,0,unlabeled,NULL,Var,clickx,Final,149.576.632,1590592074542,Value at the end of the trial
    1590592091,MD5HASHEDIP,PennController,0,0,unlabeled,NULL,Var,clicky,Final,276.329.321,1590592074542,Value at the end of the trial

    Jeremy

    in reply to: Questionnaire with questions on the same page #5435
    Jeremy
    Keymaster

    Hello Andrea,

    Since you basically want to repeat the same series of commands multiple times, you can create a function that takes your number/question as an input and outputs the commands, like this:

    question = (number,text) => [
        newText("Question-"+number, text)
        ,
        newScale("Rating-"+number, "Strongly Disagree",  "Disagree", "Neither Agree or Disagree", "Agree", "Strongly Agree")
            .log()
            .labelsPosition("top")  
            .before( getText("Question-"+number) )
            .size("auto")
            .print()
    ]

    Once this is in place, you can use it like this:

    Template("wbsi.csv", variable =>
      newTrial("WBSI",
        ...question(variable.Number1, variable.Question1)
        ,
        ...question(variable.Number2, variable.Question2)
        ,
        ...question(variable.Number3, variable.Question3)
        ,
        newButton("next", "Next")
            .print()
            .wait()
      )
    )

    Jeremy

    in reply to: Reference error for audio recording #5430
    Jeremy
    Keymaster

    Hi,

    Could you send me the link to your experiment? Were you able to successfully record audio with your script before last night’s update?

    Jeremy

    in reply to: While loop compatability? #5422
    Jeremy
    Keymaster

    Hello Callie,

    Your script will first read the Image element lines and accordingly add a 650×452 image to the page using the file specified by variable.ImgFile, then it will read the Key lines and accordingly listen to keypresses on F or J and pause its execution there until one of the two keys has been pressed, because of the wait command. Only after a keypress has happened will it release its execution and reach the commands below, starting with the newSelector line. This means that it will reach the Timer element’s wait command only after F or J has been pressed, at which point the script will wait 2 seconds before releasing its execution and reaching the clear and Selector’s test commands.

    You probably don’t want to use a Selector element in your case: Selectors take elements that are displayed on the page and make them clickable. Because a Key element is not something that you can click on, your Selector will effectively never complete.

    What you want to do instead is use callback on your Key element, so that the commands that you put in the callback are executed only after the F or J key is pressed, but without pausing the execution of your script so it can immediately launch the Timer element:

    newTrial("keySelection"
        newImage(variable.ImgFile)
            .size(650,452)
            .print()
        , 
        newKey("FJ")
            .log()
            .callback( getTimer("timerout").stop() )
        ,
        newTimer("timerout", 2000)
            .start()
            .wait() // pause execution until Timer stops
        ,
        clear()
        ,
        getKey("FJ").test.pressed()
            .success( newText(" ").print() )
            .failure( newText("Too slow!").print() )

    Jeremy

    Jeremy
    Keymaster

    Hi Nickolas,

    I hadn’t noticed that the results were sent anyway. You should have the same MD5-hased IP each time the results are sent, but a different ReceptionTime, so you won’t be able to use those to discriminate redundant results lines from the same participant form results lines from two participants taking your experiment on the same machine with the same browser. If you log a uniquely identifying ID otherwise then that’s not a problem.

    Also keep in mind that redundancy might make your results files grow quite fast, which could be a problem depending on the situation. The size of your results files counts toward calculating your quota on the PCIbex Farm, for example.

    All that being said, you can add this bit of code to your script to disable the specific prompt:

    const oldAlert = window.alert;
    window.alert = function(message) {
        if (message=="WARNING: Results have already been sent once. Did you forget to set the 'manualSendResults' config option?")
            return;
        oldAlert.call(this,message);
    }

    Jeremy

    in reply to: Behavioural task score #5403
    Jeremy
    Keymaster

    Hello Zoë,

    Your script runs the test on your Key element as soon as it creates it (well, just after setting it to log) so it will always fail: there’s no time to press any key in the millisecond(s) between the creation of the Key element and the evaluation of the test. You want to run that test in the callback, whose content is evaluated after a keypress (you can pass a series of commands in a callback):

        newKey("question1", "FJ ")
            .log()
            .callback(
                getText(row.item).remove(),
                getText("instruction").remove(),
                getTimer("window").stop(),
                getKey("question1").test.pressed(row.correctKey)
                    .success(getVar("score").set(v=>v+1))
            )

    About the Var element, I reckon things are tricky there, and actually I should probably change that, but technically within a trial you cannot refer to a named element that hasn’t been declared in that same trial. In other words, any get* command must be preceded by a corresponding new* command. So here is what you should do for your score_time trial:

    newTrial("score_time",
        newVar("score").global()
        ,
        newText("Your score on the recall task was: ")
            .after(newText("").text(getVar("score")))
            .print()
            .wait()
    )

    Jeremy

    in reply to: Page loading problems #5305
    Jeremy
    Keymaster

    Since you’re hosting your ZIP file outside the Ibex Farm, it shouldn’t affect it at all. What will happen however is that, whenever you reach a trial whose resources have not been preloaded yet, PennController will give it another 2 minutes (if I remember correctly) before starting the trial. If the resources are still not preloaded after those two minutes, the trial will start anyway: if you have a wait on an Audio element whose file did not preload, then your trial will never end.

    If your design has a multi-part structure, you could consider splitting your resources across as many archive files as you have parts, and insert CheckPreloaded before a part (specifying the corresponding label-s) to only check for its resources.

    Jeremy

    in reply to: Page loading problems #5303
    Jeremy
    Keymaster

    Hi,

    The function PreloadZip tells your experiment to download a ZIP file and uncompress its files into the browser’s cache so your trials can look up for resources there, but technically it doesn’t really control loading time. What you can do to control preloading time though is use CheckPreloaded to create a trial that will wait until resources are preloaded, or a delay is over, before proceeding:

    PennController.ResetPrefix(null)
    
    PreloadZip("https://.../.../myArchive.zip")
    
    Sequence("intro", "preload", /* ... */ )
    
    newTrial( "intro",
      newButton("Welcome. Click here to proceed.").print().wait() 
    )
    
    CheckPreloaded( 5 * 60 * 1000 ) // wait for up to 5 minutes
        .label("preload")
    
    // rest of your experiment

    Jeremy

    in reply to: Timing units #5301
    Jeremy
    Keymaster

    Thank you for spotting the broken link, it looks like Alex Drummond removed the PDF version of the Ibex manual.

    You can use a global Var element to keep track of a counter. Add this at the beginning of your template trials:

    newVar("counter", 0).global().set(v=>v+1)
        .test.is(v=>v%40==0)
        .success( newButton("Take a break").print().log().wait().remove() )
    ,
    

    Jeremy

    in reply to: Timing units #5298
    Jeremy
    Keymaster

    Hi Enriqueta,

    Chances are it won’t be exactly 300ms, because performance varies with different hardware and software configurations. Timer elements periodically check the browser’s current timestamp, usually 60 times per second, generally matching the display refresh rate. So running a 300ms timer will effectively take between 300 and 316ms under normal circumstances.

    Yes, the EventTime values are also expressed in milliseconds, and subtracting two of them will accordingly tell you how many milliseconds passed between the two reported events.

    Jeremy

    in reply to: While loop compatability? #5297
    Jeremy
    Keymaster

    Hi Cindy,

    Yes, you cannot freely inject some javascript inside a PennController trial. There is a Function element, but it’s more for running independent routines than to make PennController elements interact with native javascript code.

    Your case is a standard setup in PennController, here is what you can do:

    newTrial(
        newText("Left").print("right at 48vw", "middle at 50vh"),
        newText("Right").print("left at 52vw", "middle at 50vh")
        ,
        newSelector("choice")
            .add( getText("Left") , getText("Right") )
            .callback( getTimer("timewindow").stop() )
        ,
        newTimer("timewindow", 5000).start().wait()
        ,
        clear()
        ,
        getSelector("choice").test.selected()
            .success( newText("Thank you").print() )
            .failure( newText("Too slow!").print() )
        ,
        newButton("Finish").print().wait()
    )

    Jeremy

Viewing 15 posts - 1,276 through 1,290 (of 1,522 total)