Jeremy

Forum Replies Created

Viewing 15 posts - 1,336 through 1,350 (of 1,522 total)
  • Author
    Posts
  • in reply to: Recording Audio #5147
    Jeremy
    Keymaster

    Hello,

    Do you have a Sequence command anywhere in your script? If so, you need to be explicit about when you want the InitiateRecorder trial to run. So for example you could do this (I’m shortening the commands from your code, consistently with PennController 1.7):

    Sequence( "init" , randomize("choice1") )
    
    InitiateRecorder("https://XXXXXXXXXXXXXX/ibex.php")
      .label("init")
    
    Template( "XXXXXXXXX.tsv",
        row => newTrial( "choice1"
        ,
        newVoiceRecorder("recorder")
          .print()
        ,  
        newText("nameInput", row.Sentence )
          .center()
          .print()
        ,  
        // Adding a validation button (feels wrong to end the trial when the slider gets released)
        newButton("validate", "Next")
         .center()
         .print()
         .wait()
      )
    )

    Jeremy

    in reply to: Choosing subet of items to present #5146
    Jeremy
    Keymaster

    Just to double-check: your table defines 90 filler items whose ‘number’ label goes from 1 to 90, right?

    When I test it I do get items whose number is over 45, but they indeed seem massively underrepresented. It seems to improve a bit when changing the > comparison when randomly sorting the indices for a >= comparison

    Here’s my full script if you want to compare with yours:

    PennController.ResetPrefix(null)
    
    let myTable = "type,number,text\n"
    
    for (let i = 0; i < 90; i++)
        myTable += "filler,"+Number(i+1)+",textfiller"+i+"\ntest,"+Number(i+1)+",texttest"+i+"\n";
    
    AddTable("myTable", myTable)
    
    Sequence( randomize(anyOf(startsWith("test"),startsWith("filler"))) )
    
    numberRows_fillers = 90; 
    numberDraws_fillers = 45; 
    randomIDs_fillers = [...new Array(numberRows_fillers)].map((v,i)=>i).sort(v=>Math.random()>=0.5).splice(1,numberDraws_fillers); 
    
    Template( GetTable("myTable").filter( r => r.type=="filler" && randomIDs_fillers.indexOf(Number(r.number))>-1 ),
        row => newTrial( "filler-"+row.number , newText("filler").print(),newButton(row.text).print().wait() )
    )
    
    Template( GetTable("myTable").filter( r => r.type=="test" && randomIDs_fillers.indexOf(Number(r.number))>-1 ),
        row => newTrial( "test-"+row.number , newText("test").print(),newButton(row.text).print().wait() )
    )

    Jeremy

    in reply to: Partial randomization of trial sequence #5141
    Jeremy
    Keymaster

    Ugh, the forums mishandled the rendering of a < symbol, which I missed when replacing them with HTML entities, so you got < = on line 8 instead of <=

    I edited my message to fix the code, just update the content of your PartialRandom.js file with it and it should work

    Sorry about that

    Jeremy

    in reply to: Partial randomization of trial sequence #5134
    Jeremy
    Keymaster

    Hi,

    What you are asking for is a little advanced, so I wrote a snippet for it. Add a .js file to your Controllers (js_includes) folder containing this code:

    let subsequence;
    let repeat;
    
    (function (){
        function Times(n,predicate){
            this.predicate = predicate;
            this.n = n;
        }
        
        repeat = (predicate,n) => {
            if (n<=0) return predicate;
            else return new Times(n,predicate);
        };
        
        function Subsequence(...predicates) {
            this.args = [];
            this.times = [];
            for (let i = 0; i < predicates.length; i++){
                let predicate = predicates[i];
                if (predicate instanceof Times){
                    this.args.push(predicate.predicate);
                    this.times.push(predicate.n);
                }
                else{
                    this.args.push(predicate);
                    this.times.push(1);
                }
            }
        
            this.run = function(arrays) {
                let remainingPredicates = arrays.length;
                let newArray = [];
            
                while (remainingPredicates > 0){
                    for (let n = 0; n < arrays.length; n++){
                        let predicate = arrays[n];
                        for (let times = 0; times < this.times[n] && predicate.length > 0; times++)
                            newArray.push( predicate.pop() );
                        if (predicate.length===0) remainingPredicates--;
                    }
                }
                return newArray;
            };
        }
        
        subsequence = (...predicates) => new Subsequence(...predicates);
    }());

    Then you can use it like this in the Sequence command:

    Sequence( "intro" , subsequence( repeat(randomize("main"),2) ,"sep" ) , "end" )

    The command subsequence will return a series of interleaved trials with corresponding labels, exhausting them all, so if you simply had subsequence("main","sep") you would get a series of [main,sep,main,sep,main,sep,...] trials containing all the main and sep trials.

    You can use the repeat command inside the subsequence command to control how frequently your interleave things, as illustrated above. You could even do something like this: subsequence( repeat("main",2) , repeat("sep",2) ) and you would get series of 2 main trials followed by 2 sep trials (ordered as defined in your script, since there’s no randomize command here).

    Note that if you have more trials than necessary, they will be appended at the end of the subsequence: say you have 41 main trials and 22 sep trials, then subsequence( repeat(randomize("main"),2) ,"sep" ) will give you a series of 20 times [2 main trials followed by 1 sep trial] and then 1 remaining main trial followed by 2 remaining sep trials.

    Jeremy

    in reply to: 'Failed to save' error #5133
    Jeremy
    Keymaster

    Hi Umesh,

    I’m glad to read that you find the autocompletion feature helpful, I was wondering about that

    Yes, increased traffic sort of saturates the RAM of the server, which in turn fails to process updates to the code (I haven’t heard of results failing to get saved so far though, thankfully)

    Jeremy

    in reply to: 'Failed to save' error #5123
    Jeremy
    Keymaster

    Hi Umesh,

    You’re not doing anything wrong, I experienced the same problem over the last few days. I am actively working on a new version of the PCIbex Farm that should fix this issue, and trying to find a temporary workaround in the meantime, which will likely involve rebooting the servers and, as a result, possibly interrupting ongoing data collection (which is why I keep delaying it taking action…).

    I apologize for this. In the meantime, you can try editing your code locally using a text editor and uploading the file back to your project. If this still does not work for you, consider hosting your experiments on the original Ibex Farm (note though that the Ibex Farm is not on a secure domain, so you won’t be able to access microphones and webcams, and you cannot upload resources directly to chunk_includes there).

    Jeremy

    in reply to: Accuracy of reaction times recorded by pcIBEX #5115
    Jeremy
    Keymaster

    Hi,

    I’m not aware of any paper systematically assessing the accuracy of reaction times in Ibex (or PCIbex) but I wouldn’t be surprised that some published papers reported self-paced-reading experiments designed with Ibex.

    What I can tell you though is that you shouldn’t worry about varying speeds of the Internet connections: at least with (PC)Ibex, but I suspect other online softwares do the same thing, all the content of the experiment is downloaded at the beginning (modulo images/audios/etc. in pure Ibex–PCIbex does handle media preloading). So once a participant starts an experiment, they won’t need to communicate with the server until they submit their results at the very end.

    The concerns mostly come from each participant’s local environment: do they have lots of tabs open in their browser? Do they have an old, weak processor? What kind of keyboard are they using? If all goes well, most participants won’t encounter performance issues, and then maybe you’ll be able to detect an effect size of 25ms.

    If you can afford it, maybe run a small control experiment where you know there to be an effect of comparable size between two conditions to get a sense of what you’ll get?

    Hopefully other people here can point you to relevant papers

    Jeremy

    in reply to: Logging order of elements #5108
    Jeremy
    Keymaster

    Hi,

    There is no simple built-in command or option for that at the moment, so you have to check the order yourself:

    newTrial(
        newButton("option1", variable.answer1)
        ,
        newButton("option2", variable.answer2)
        ,
        getCanvas("canvas", 500,100)
            .settings.remove(getText("listen"))
            .settings.add(150, 180, getButton("option1"))
            .settings.add(350, 180, getButton("option2"))
            .print()
        ,
        newVar("leftbutton").global().set("option1"),
        newVar("right").global().set("option2")
        ,
        newSelector()
            .settings.add(getButton("option1"), getButton("option2"))
            .shuffle()
            .settings.log()
            .wait()
            .test.index(getButton("option1"),0)
            .failure(
                getVar("leftbutton").set("option2"),
                getVar("right").set("option1")
            )
    )
    .log("leftbutton", getVar("leftbutton"))
    .log("rightbutton", getVar("rightbutton"))

    Jeremy

    in reply to: Centering mouse cursor at the start of the trial #5107
    Jeremy
    Keymaster

    Hi Ana-Maria,

    The script cannot take control of the mouse for security reasons, but what you can do is print a small button where you want the mouse to be and reveal your pictures only after it is clicked, for example:

    newButton("Start").print("center at 50vw","middle at 50vh").wait().remove()
    ,
    newSelector("image")
    ,
    defaultImage.size(200,200).selector("image")
    ,
    newImage("topleft.png").print("right at 45vw","bottom at 45vh"),
    newImage("topright.png").print("left at 55vw","bottom at 45vh"),
    newImage("bottomleft.png").print("right at 45vw","top at 55vh"),
    newImage("bottomright.png").print("left at 55vw","top at 55vh")
    ,
    getSelector("image").wait()

    Jeremy

    in reply to: Collect response via response button OR key press #5103
    Jeremy
    Keymaster

    Hi!

    I admit that it’s a bit tricky, but as alluded to on this page of the tutorial, the way to represent a click OR a keypress is to use a Selector, so in your case this is what you would do:

        newText("Press SPACE to continue ")
            .center()
       ,
        newButton("Space")
            .print()
            .center()
       ,
        newSelector()
            .add(getButton("Space"))
            .keys( " " )
            .wait()
    in reply to: Variable Math and Text Concatenation #5099
    Jeremy
    Keymaster

    Ah, sorry, there were supposed to be HTML entities in my code above but I guess the forum somehow stripped them away

    I should make initial and last space characters count in the next releases of PennController. In the meantime, replace the space characters at the beginning or at the end of a Text element with &nbsp;

    Jeremy

    in reply to: Variable Math and Text Concatenation #5095
    Jeremy
    Keymaster

    Hi,

    Math operations on Var elements are not totally straightforward if you’re trying to keep plain javascript minimal, here’s an example:

    newTrial(
        newVar("dividend"),newVar("divisor")
        ,
        newTextInput()
          .before( newText("Dividend:") )
          .print()
          .wait()
          .setVar("dividend")
          .remove()
        ,
        newTextInput()
          .before( newText("Divisor:") )
          .print()
          .wait()
          .setVar("divisor")
          .remove()
        ,
        newVar("result")
          .set( getVar("dividend") )
          .set( v=> v / getVar("divisor").value )
        ,
        newText()
          .text( getVar("dividend") )
          .after( newText(" divided by " ) )          // see msg below re. spaces
          .after( newText().text(getVar("divisor")) )
          .after( newText(" makes ") )                // see msg below re. spaces
          .after( newText().text(getVar("result")) )
          .print()
        ,
        newButton("ok").print().wait()
    )

    As you can see I had to use .value to retrieve the value of another Var element inside the v => ... expression.

    The code above should also give you a good idea of how to concatenate Text elements.

    Jeremy

    • This reply was modified 5 years, 2 months ago by Jeremy. Reason: Made the HTML entities explicit
    • This reply was modified 5 years, 2 months ago by Jeremy. Reason: added comments about nbsp
    in reply to: Reaction times of a selector response #5093
    Jeremy
    Keymaster

    Hi,

    As you found out, you cannot pass a Var element to initiate another element. This is because all the new* commands (but not their corresponding blocks of commands) are evaluated at the very beginning of the experiment, at which point the Var elements have not been set in the yet-to-come flow of trial events.

    What you can do instead is use the .text command of the Text element, like this:

    newVar("RT").set( v=>Date.now() )
    ,
    newButton("Click!").print().wait()
    ,
    getVar("RT").set( v=>Date.now()-v )
    ,
    newText().text( getVar("RT") ).print()

    Jeremy

    in reply to: Multiple choice buttons and recording responses #5088
    Jeremy
    Keymaster

    Hi!

    You need to use the .test.complete and .warn commands on your Html element to achieve what you want (follow the links to see illustrations in the documentation).

    Jeremy

    in reply to: Multiple choice buttons and recording responses #5086
    Jeremy
    Keymaster

    Hello,

    Do you see any error messages in the Debug window when you add the .log command(s) causing your experiment to crash? Note that you won’t get any expression starting with variable. to work in a .log attached to a newTrial that is not embedded in a Template command, as those expressions have a meaning as long as they point to a row in your table.

    Also, PCIbex current does not generate individual files for your participants. Instead, it aggregates the results of all your participants in the CSV-formatted results and JSON-formatted raw_results files. Note that the CSV file results also contains comment lines prefixes with the # character: you can freely delete those lines if you want.

    As for the link at the end of your experiment, it really depends on how you recruit your participants. It’s quite unlikely that you’d really want to redirect participants who have completed your experiment to the same experiment again. Some platforms like Prolific or SONA let you provide your participants with a confirmation link to insert at the end of your experiment that will automatically validate your participants’ submissions. You can also decide to track your participants’ submissions manually if that’s an option you’d be more comfortable with, and I see you’re already collecting IDs about your participants, so it’s definitely something you can do (keep in mind though that rules and/or laws usually apply to collection and storage of identifying information, such as first and last names for example).

    Jeremy

Viewing 15 posts - 1,336 through 1,350 (of 1,522 total)