Jeremy

Forum Replies Created

Viewing 15 posts - 421 through 435 (of 1,522 total)
  • Author
    Posts
  • in reply to: Missing data (Failed submission) #8180
    Jeremy
    Keymaster

    Hi Monica,

    I see 13 submissions for that experiment, the four most recent ones received on April 27, April 28, May 6 and May 12. Each of your 13 submissions has between 54 and 58 rows

    Were you expecting another 4 submissions (for a total of 17 submissions)?

    Jeremy

    in reply to: Only even groups of stimuli work #8178
    Jeremy
    Keymaster

    Hi Daria,

    The file SV_fillers.csv in your first experiment contains three empty rows at the end: those form a second group besides “Fil,” and Template cycles through the two groups based on the counter, just the way it cycles through the groups in SV_stimuli_AC.csv. When it runs the second group, the rows generating the filler trials are empty, so row.sentence is not defined, thus raising the error you report. You can also see that in the debugger’s Sequence tab: in problematic runs, you only have two filler items, associated with “PennController::SV_fillers.csv:33” and “PennController::SV_fillers.csv:34,” 33 and 34 referring to the 33rd and 34th rows (excluding the header row) from SV_fillers.csv—the very last row is ignored, as it is truly empty: unlike the two previous ones, it doesn’t even contain a linebreak character

    Delete the extra rows in your CSV file, and also maybe just remove the “group” column entirely from that table, since you want to use all the rows for all participants anyway

    Jeremy

    in reply to: Jump to top of page when new item is loaded #8175
    Jeremy
    Keymaster

    Hi Nadine,

    I see, this happens when a new element gets added to the page: PennController tries to scroll down to make it visible, since otherwise when elements gets added under ones that already occupy the page’s full vertical viewport, participants might not notice that a vertical scrollbar just appeared and that an element now lies below the ones visible on the page

    Anyway, just add newFunction( () => window.scrollTo(0,0) ).call() immediately after you print your Text element, that should do the trick

    Jeremy

    Jeremy
    Keymaster

    Huh, you’re right, I get the same issue on Chrome too. I don’t know why the change did the trick for the first demo link…

    In any case, the first diagnostic stands: the problem essentially arises when you have two Audio elements that play the same file. You can easily work around that by replacing all the occurrences of newAudio("audio_2", row.Audio_2)) with (row.Audio_2==row.Audio_1?getAudio("audio_1"):newAudio("audio_2", row.Audio_2)) in your code

    Also, I don’t get the “async” error in Chrome: what version of the browser are you using?

    Jeremy

    in reply to: Issue loading experimental materials #8171
    Jeremy
    Keymaster

    Hi Lily,

    I was able to reproduce the issue the second time I took your experiment. I inspected my webconsole and noticed multiple 403 errors: those are called “Forbidden errors,” which means that the server will not accept the request. This means that the servers hosting your audio and image files sometimes refuse to serve some of them

    Unfortunately there is not much you can do on the PCIbex Farm’s end, since it’s up to the host server to decide whether to accept the farm’s requests. It could be that admins there have added a check that blocks requests when too many are received in too short a time window. One option you could consider is to compress your audio files in a zip archive (and also your image files if you’d like, in the same or in a separate zip file) and fetch that instead of each audio file individually, using PreloadZip in addition/instead of AddHost. You’ll find more info on the procedure in the documentation. You might need to contact IT for assistance, depending on whether their webserver accepts .htaccess configuration files

    NB: I recommend you share your project’s demonstration link rather than the production link, or you might end up with noise in your results file. Also, I need demonstration links, which come with a very useful debugger, for troubleshooting (which, in this case, I retrieved by looking up associations in the farm’s database)

    Let me know if you have any questions

    Jeremy

    in reply to: Jump to top of page when new item is loaded #8170
    Jeremy
    Keymaster

    Hi Nadine,

    I must be missing something, because the view shouldn’t be able to scroll down at all, since normally new trials clear the content of the page and so there’s just no content overflowing vertically to make it possible to scroll down

    Do you have a link to your project?

    Jeremy

    in reply to: Issue loading experimental materials #8165
    Jeremy
    Keymaster

    Hi,

    Do you have a link to your project that I could inspect? Also, take a look at this thread: Chrome seems to have trouble preloading audio file, see if your situation improves after implementing the trick I describe on that thread

    Jeremy

    in reply to: Display currently selected value of slider scale #8164
    Jeremy
    Keymaster

    Hi Nadine,

    Here’s how to do it:

    newTrial(
        newText("scaleValue", "NA").before( newText("Score: ") )
        ,
        newScale("myScale", 7)
            .slider()
            .callback( getText("scaleValue").text(getScale("myScale")) )
            .print()
        ,
        getText("scaleValue").print()
        ,
        newButton("Next").print().wait()
    )

    Note that values start with 0, so if you want to display a score starting with 1, you’ll need to use a Var element:

    newTrial(
        newVar("scaleVar", 0)
        ,
        newText("scaleValue", "NA").before( newText("Score: ") )
        ,
        newScale("myScale", 7)
            .slider()
            .callback( 
                getVar("scaleVar").set(getScale("myScale")).set(v=>parseInt(v)+1)
                ,
                getText("scaleValue").text( getVar("scaleVar") )
            )
            .print()
        ,
        getText("scaleValue").print()
        ,
        newButton("Next").print().wait()
    )

    Jeremy

    Jeremy
    Keymaster

    Hi Aiden,

    I’m sorry for not catching this problem: I use Firefox as my default browser, and even though I usually check that things also work on Chrome, I never tested playing back the same audio file within a single trial in two different Audio elements on Chrome. What happens is, browsers usually prevent webpages from automatically playing back audio/video files until the user has interacted with the page. Both Firefox and Chrome do that, which messes with the preloading of the audio files. For some reason, when that happens but Firefox still ultimately manages to preload an audio file, PennController notifies all Audio elements using the same audio file that it’s been preloaded. When the same thing happens on Chrome, however, only one of the two elements gets notified

    I’ll work on fixing that in the next release of PennController. In the meantime, you can override the definition of the Audio element to make it wait for a click on (= interaction with) the page before preloading the audios. I tested this solution on Chrome and it seems to work well

    The very first thing to do is insert an intro trial to make participants click on the page before they get to the first trial, so that the browser detects an interaction with the page and can safely start preloading the audio files. Something like:

    Sequence("intro","trials_1.2")
    
    newTrial("intro", newButton("Start").print().wait() )

    Then, you need to download this file (use your browser’s Save function–make sure to keep the filename PennElement_audio.js). Upload this file to your project’s Modules folder: it will effectively override the Audio element defined in your project’s copy of PennController.js (which will give you an error in the debugger saying “Element type Audio defined more than once” when you run your experiment, but you can just ignore it)

    Now, you’ll need to make some edits to that file. First, you should add this line at the very top: const hasClicked = new Promise(r=>document.addEventListener("mousedown", r));

    Then, since you are using PennController 2.0, you should delete (or comment out) the block of lines starting with this.resource = PennEngine.resources.new(file, function(uri, resolve){ up to (the first occurrence of) }, addHostURLs);. Notice all the lines coming after that prefixed with //, starting with // this.resource = PennEngine.resources.fetch(file, function(resolve){: you need to uncomment them all (ie. delete // at the beginning) up to and including // }, addHostURLs);

    Finally, replace this.resource = PennEngine.resources.fetch(file, function(resolve){ with this.resource = PennEngine.resources.fetch(file, async function(resolve){ and just after that line, and before this.object = new Audio(); // Creation of the audio using the resource's value, insert a line with just await hasClicked;

    Once you’ve proceeded to those edits, the first lines of your copy of PennElement_audio.js should be:

    const hasClicked = new Promise(r=>document.addEventListener("mousedown", r));
    
    // AUDIO element
    /* $AC$ PennController.newAudio(name,file) Creates a new Audio element $AC$ */
    /* $AC$ PennController.getAudio(name) Retrieves an existing Audio element $AC$ */
    window.PennController._AddElementType("Audio", function(PennEngine) {
    
        const RATIO_PRELOADED = 0.95;
    
        // This is executed when Ibex runs the script in data_includes (not a promise, no need to resolve)
        this.immediate = function(id, file){
            if (typeof id == "string" && file===undefined)
                file = id;
            let addHostURLs = !file.match(/^http/i);
    
            this.resource = PennEngine.resources.fetch(file, async function(resolve){
              await hasClicked;
              this.object = new Audio();               // Creation of the audio using the resource's value

    Take your experiment in Chrome, and it should now preload fine

    Let me know if you encounter issues or have any questions

    Jeremy

    in reply to: NULL results, ignoring trials, and error messages #8159
    Jeremy
    Keymaster

    Hi Meri,

    1) If you look at the comments in your results file (lines starting with #) you will see that the last column’s name is “Comments”: in your case, you get NULL in every line because there’s no comments to report. It’s not always the case: for example, if you use log on a Scale element without wait and the trial ends without the participant making any selection on the scale, you will get a line where that column reads “No selection happened”

    2) One or multiple buffering events happening in the middle of a trial could indicate that the participant encountered difficulty playing back the audio, which could matter for some time sensitive tasks like primed lexical recognition. If that’s not relevant in your case, then you don’t have to log that information

    3) I’m not sure what you are referring to. I tried opening your experiment multiple times, running as different groups, and the Sequence tab of the debugger always showed 25 trials. Maybe that used to happen in an earlier version of your project that had an extra value in the group column of the CSV table, or where there was a typo somewhere in the script that prevented part of the code from being evaluated?

    4) This error message comes from the original Ibex: whenever an error prevents the script from filling the shuffleSequence variable (and there are many types of errors that can have that consequence, particularly syntax typos like missing a comma somewhere, which make the javascript parser crash) then Ibex complains that there’s no items in the running order. PennController adds a layer trying to catch as many errors as it can detect before Ibex kicks in, but some errors (again, particularly syntax errors) are harder to handle

    Best,
    Jeremy

    in reply to: Code not updating #8153
    Jeremy
    Keymaster
    in reply to: Switching between multiple tasks #8151
    Jeremy
    Keymaster

    So let me summarize and try to clarify the idea to make sure we’re on the same page

    If you were to use a generalized version of latinMix with three labels (say, flanker-sentence, flanker and sentence) you would need to create three types of trials: one type containing only the sentence task (labeled sentence), one containing only the flanker task (labeled flanker) and one containing both the flanker and then the sentence tasks (labeled flanker-sentence). You say that only the latter would correspond to experimental items with a preset flanker-sentence association. This suggests to me that for those (flanker-sentence, mixed-type trials) you would need a table where each line has references for both the sentence and the flanker tasks, whereas for the former two (sentence and flanker, homogeneous-type trials) you would need two tables, one that only contains references for the reference task and one that only contains references for the flanker task

    Now, say you have 21 rows in the flanker-sentence table, 21 rows in the sentence table and 21 rows in the flanker table. You would get a total of 42 iterations of the flanker task in your final experiment (half from the mixed trials labeled flanker-sentence and half from the homogeneous trials labeled flanker) and a total of 42 iterations of the sentence task (same logic). I use those numbers because the number of combinations that a generalized version of latinMix would output for three labels is 21. Some of the triplets would contain only three task iterations (mixed or not) but some would contain four iterations, some five and one would even contain six (the one that draws exclusively from flanker-sentence, namely flanker-sentence-flanker-sentence-flanker-sentence). Obviously you would end up with an over-representation of the flanker-sentence subsequence, because we started with 21 rows in each table but one of them is used to generate flanker-sentence while the other two generate only flanker or only sentence. As you can see this all becomes very complicated very quickly

    Now that I have a better idea of what you want, I do not think that using any version of latinMix will make your life simpler. I think what would help is determine how many experimental items you will have, and how many of those will use each type of flanker image (as I understand it, you have four possible flanker images). Then, you would need to determine how many iterations of each task you want in the final experiment, and whether/how you want to balance your design. For example, if you have 8 experimental trials (2 where the sentence is preceded by LL.png, 2 by LR.png, 2 by RL.png and 2 by RR.png) do you also want to have 8 corresponding filler trials? That would give you 16 trials where the sentence is preceded by a flanker task. Would you want to then have another 16 filler trials where the sentence is *followed* by a flanker image (4 with LL.png, 4 with LR.png, 4 with RL.png and 4 with RR.png)? Finally, how many more independent iterations of each task type do you want to mix in? And do you want to license sequences that would end the experiment on a flanker task?

    The situation would be a little simpler (although not much) if, instead of randomly generating the fillers during runtime, you defined a pseudo-randomized set of filler items in your tables to compensate your experimental items, as you would simply reference all the sentences and images by hand and the newTrials would be pretty generic

    Re-using the flanker and sentence functions I defined in my previous message (and assuming a ‘yes’ answer where it applies to all the questions I raise above) you could write a single function to generate each type of trials without having to repeat whole pieces of code, eg:

    trialOfType = (type,row) => newTrial(type,
        newCanvas("FixationCanvas", 1800, 900)
          .add(675,400, newImage("Fixation.png"))
          .scaling("page")
          .print("center at 50vw","middle at 50vh")
        ,
        newTimer("wait", 1000).start().wait()
        ,
        clear()
        ,
        ...( type=="flanker-sentence"||type=="flanker" ? flanker(row) : sentence(row) )
        ,
        ...( type.match('-') ? [
          getTimer("wait").start().wait()
          ,
          getCanvas("FixationCanvas").print("center at 50vw","middle at 50vh")
          ,
          getTimer("wait").start().wait()
          ,
          clear()
          ,
          ...( type=="flanker-sentence" ? sentence(row) : flanker(row) )
        ] : [ null ] )
    )
    .setOption("hideProgressBar",true)
    
    Template("flanker-sentence_experimental.csv", row => trialOfType("flanker-sentence", row))
    Template("flanker-sentence_filler.csv", row => trialOfType("flanker-sentence", row))
    Template("sentence-flanker.csv", row => trialOfType("sentence-flanker", row))
    Template("flanker.csv", row => trialOfType("flanker", row))
    Template("sentence.csv", row => trialOfType("sentence", row))
    
    Sequence( rshuffle("flanker-sentence", "sentence-flanker", "flanker", "sentence") )

    Jeremy

    in reply to: Switching between multiple tasks #8149
    Jeremy
    Keymaster

    There is something that still confuses me when you say “which flanker image (i.e. the direction of the middle arrow) would precede the sentence task,” because the flanker image does not (immediately) precede any sentence in a flanker-flanker pair, and the sentence is not (immediately) preceded by any flanker image in a sentence-sentence pair. You say that my second solution should work, so I presume that you do not want sentence-sentence or flanker-flanker pairs in the end?

    The latinMix function takes two sets of labeled trials, say “A” and “B,” and outputs a sequence of pairs of trials evenly distributed over A-B, B-A, A-A and B-B. For example, if you have four A trials and four B trials, you could get A(1)-B(4)-A(3)-A(2)-B(1)-A(4)-B(3)-B(2). I hard-coded the function to output such a crossing from strictly two labels, but it could in theory be modified to accommodate any number of labels. For example, with three labels A, B and C, a generalized function would output an even distribution of triples of trials over A-B-C, A-C-B, B-A-C, B-C-A, C-A-B, C-B-A, A-A-A, A-A-B, A-A-C, A-B-B, A-C-C, B-A-A, B-B-A, B-B-B, B-B-C, B-B-B, C-A-A, C-B-B, C-C-A, C-C-B and C-C-C. Is that what you want?

    Jeremy

    in reply to: Switching between multiple tasks #8147
    Jeremy
    Keymaster

    Hi,

    I thought you wanted the sentence vs flanker tasks to be totally independent, because you stated that half of your items need to form sentence-sentence or flanker-flanker subsequences. I’m not sure how you would achieve that with a table where each line has references for both a sentence and a flanker task: it would seem to me that such a table would systematically pair a sentence task with a flanker task, regardless of whether the former precedes or follows the latter, but that it wouldn’t really allow for sentence-sentence or flanker-flanker pairs. Or am I missing something?

    The latinMix function I proposed accepts only two arguments (see assert(arrays.length == 2, "Wrong number of arguments (or bad argument) to LatinMix");) so you will get an error if you try Sequence( latinMix(randomize("flanker-sentence"), randomize("sentence"),randomize("flanker")) ). I also don’t know what output exactly you would expect to get from that, since in your first message you listed four types of pairs resulting from crossing two types of tasks, and this is exactly what latinMix does

    To reiterate, I don’t know how one could modify the code I suggested, or come up with another implementation, because I don’t know how to determine the sentence-sentence and flanker-flanker pairs if you need to systematically associate one sentence with one flanker task (ie. how would you decide which two sentences or which flanker images to associate to form pairs?)

    Note that if all you want really are sentence-flanker and flanker-sentence pairs, then once you put all those pairs one after the other in a sequence of trials, you will for example get a general sequence like sentence-flanker-flanker-sentence-flanker-sentence-sentence-flanker-etc., which does technically contains both sentence-sentence and flanker-flanker subsequences, because sometimes you’ll have a pair that starts with one task type following another pair that ends with that same task type. If that’s what you’re after, then all you need to do is alternate which bit of code comes first in your existing newTrial, eg:

    // Flanker task
    flanker = row => [
      newTimer("RT",1000).start()
      ,
      newCanvas("FlankerCanvas", 1800, 900)
        .add(675,400, newImage(row.FlankerImage))
        .scaling("page")
        .print("center at 50vw","middle at 50vh")
      ,
      newKey("flankerFJ", "FJ")
        .log("first")
        .callback(getTimer("RT").stop())
      ,
      getTimer("RT").wait()
      ,
      getKey("flankerFJ").disable()
      ,
      getCanvas("FlankerCanvas").remove()
    ]
    // Sentence task
    sentence = row => [
      newAudio("audio", row.AudioFile).play()
      ,
      defaultImage.size(225, 225)
      ,
      newImage("TL", row.TL)
      ,
      newImage("TR", row.TR)
      ,
      newImage("BL", row.BL)
      ,  
      newImage("BR", row.BR)
      ,
      newCanvas("visualWorld", 1800, 900)
        .add(    "left at 50px" ,     "top at 50px" , getImage("TL"))
        .add(    "left at 50px" , "bottom at 850px" , getImage("BL"))
        .add( "right at 1700px" ,     "top at 50px" , getImage("TR"))
        .add( "right at 1700px" , "bottom at 850px" , getImage("BR"))
        .scaling("page")
        .print("center at 50vw","middle at 50vh")
      ,
      getAudio("audio").wait("first")
      ,
      newSelector().add(getImage("TL"), getImage("BL"), getImage("TR"), getImage("BR"))
        .wait()
        .log()
      ,
      getCanvas("visualWorld").remove()
      ,
      newText("comp-question", row.Question)
        .cssContainer({"font-size": "160%", "color": "black"})
        .center()
        .print()
      ,
      newText("yesno", "<p>YES &nbsp; &nbsp; &nbsp; NO</p>")
        .cssContainer({"font-size": "160%", "color": "black"})
        .center()
        .print()
      ,
      newKey("SentenceFJ", "FJ").log("first").wait().disable()
      ,
      getText("comp-question").remove()
      ,
      getText("yesno").remove()
    ]
    
    ABorBAs = []    // Browse the table once to fill ABorBAs with alternating 0s and 1s
    Template("flanker-sentence.csv", row => newTrial("__dummy__", ABorBAs.push(ABorBAs.length%2)) )
    Template("flanker-sentence.csv", row =>
      newTrial( "test" ,
        fisherYates(ABorBAs)    // Shuffle ABorBAs
        ,
        ABorBA = ABorBAs.pop()  // Pick the last entry from ABorBAs
        ,
        newCanvas("FixationCanvas", 1800, 900)
          .add(675,400, newImage("Fixation.png"))
          .scaling("page")
          .print("center at 50vw","middle at 50vh")
        ,
        newTimer("wait", 1000).start().wait()
        ,
        clear()
        ,
        ...(ABorBA?flanker(row):sentence(row))  // Flanker if 1, sentence otherwise
        ,
        getTimer("wait").start().wait()
        ,
        getCanvas("FixationCanvas").print("center at 50vw","middle at 50vh")
        ,
        getTimer("wait").start().wait()
        ,
        clear()
        ,
        ...(ABorBA?sentence(row):flanker(row))  // Sentence if 1, flanker otherwise
      )
      .log("flankerFirst", ABorBA)
      .setOption("hideProgressBar",true)
    )
    
    Sequence( randomize("test") )

    Jeremy

    in reply to: Delay in making changes #8145
    Jeremy
    Keymaster

    Hi,

    I am sorry you are encountering issues with updating your project. In case anyone is cloning their whole project just to force text edits to be reflected in the files, that is not necessary: you can copy-paste the text in a local copy, delete just the one file from your project, create it again, and paste the content back in the editor. I am working on fixing this issue, hopefully I should be able to update the farm in the coming days/weeks

    I have not encountered the audio file problem before. Did you have to upload any audio file again, or simply re-typing the filenames in the script did the trick? Had you deleted the original project when this problem occurred, or did the original project still exist while the audios from the copy wouldn’t play?

    Jeremy

Viewing 15 posts - 421 through 435 (of 1,522 total)