Pseudo-randomization of stimuli trials in a loop

**OS: macOS Catalina 10.15.6
**PsychoPy version: 2020.1.2
**Standard Standalone: YES

What are you trying to achieve?:
Pseudo-randomize trials in a loop using custom code in the builder.

What did you try to make it work?:
Checking the psychoPy source code and trying to understand a bit how things work. I copied pasted some parts to the “Beginning of the Routine” tab of the 1st routine within my loop and adjusted them to set a custom order of trials regardless of the order they appear in my excel file. I used the following code:

#This is only an example, the number of trials and their order can vary as pleased
customOrder = np.array([[2], [3], [0], [1]])  
myLoop.sequenceIndices = customOrder

The code runs the first trial based on the original order and later runs the rest of the trials in the custom order. Furthermore, the experiment seems to crash when the trial shown as the first one is about to be ran in the next loop according to the custom order. For example, if the the originally random order of the trails is 0, 1, 2, 3, (i.e. the 1st row in the stimuli list is to be run first, then the 2nd row, and so on), the code above (showing a custom order of 2, 3, 0, 1) correctly runs the first two trials (with the third and the fourth row in the stimuli list) but crashes when attempting to run the third loop with the first row. I receive the following error message:

WARNING need a bigger array for: ran

I think the problem is related to an old post that I found by googling:

Any ideas of how to make this approach work? Ideally I would have to assign the order of assign the otrder of the stimuli with myLoop.sequenceIndices = customOrder when the loop element is created before starting the first routine of the loop.


Hi There,

This is actually tricky to implement in builder because of the order in which objects are created (you probably noticed this from exporting your experiment to code).

Usually if a preset order is needed we recommend setting the conditions file to be in that order and using the ‘sequential’ loop method (if you have a new order for each participant make a set of conditions files and use something like $'conditions'+expInfo['participant']+'xlsx' in the conditions field of the loop - is there a reason that this wouldn’t be suitable for this context?


@Becca Thank you very much for the response. I think your approach would work well, although it is a somewhat messy (and time consuming) to create a different and separate conditions file per participant. I have have worked today in the same issue and I just out a way to do it quite nicely.

I defined a function in the “beginning of the experiment” tab of a code component that would take the name of excel file containing the trials as argument to load the trials in their original order, then randomize such trials based on some particular condition(s) and finally would return a list of the final order of such trials. Setting a loop in the builder to follow a “sequential order” rather than a “random order”, I called the function that I created in the “selected rows” space and the result is that psychoPy presents stimuli in the exact sequential order that my pseudo-randomization function returns.

Note that: For this approach to work, it is important to have a column in the conditions files that indexes the original sequential order of the stimuli so that once the stimuli list is correctly randomized the final order of the trials can be retrieved.

Here is the code that I used. To illustrate, I set the randomization condition to not allow two consecutive trials of the same stimuli type:

def pseudoRand(excelFile):
    # Load trials
    conditionMet = False
    # Pseudo-randomize trials
    while conditionMet == False:
        conditions = []
        for row in range(len(trials)):
            conditions.append(trials[row]["stimuliCondition"])  #Name of the conditions type column
        # Condition for pseudo-randomization: No consecutive same type
        for row in range(len(trials) - 1):
            if trials[row]["stimuliCondition"] == trials[row + 1]["stimuliCondition"]:
                conditionMet = False
                conditionMet = True
    # Save the final order of the trials
    customOrder = []
    for row in range(len(trials)):
        customOrder.append(trials[row]["originalIndex"]) #Name of the original index column
    return customOrder

Hi Miguel,

Pleased you found a solution. With regards to it being messy to make multiple conditions files. You can use code to do this.

For example, I use code to create several .csv files for counterbalancing


1 Like

@miguel_santin Hi, Miguel. Thank you for sharing your code and it was very helpful!
But for the sake of sharing some tips regarding online experiments in Pavlovia, which needs JavaScript code to be executed, I would like to share my JS code.

I was having trouble translating your code to JS code, since automatic translation in the code component got me various errors, but I finally found the way to make the code run on JavaScript.

The logic of the following code is same for Miguel’s,
but Note that

  1. excelFile must be sorted by “originalIndex” columns and well synced
  2. Code Type for the code component has to be “Both” type ( Not “Auto->JS”! )
  3. Write $pseudoRand(‘your condition file’s name.csv’) in the “selected rows” space (to call the function)
function pseudoRand(excelFile) {
    var conditionMet, tempData, conditions, customOrder, trials;
    tempData = new TrialHandler({
        psychoJS: psychoJS,
        nReps: 1, method: TrialHandler.Method.SEQUENTIAL,
        extraInfo: expInfo, originPath: undefined,
        trialList: excelFile,
        seed: undefined, name: 'tempData'});;
    trials = tempData['trialList'];
    conditionMet = false;
    while ((conditionMet == false)) {
        conditions = [];
        for (var row, _pj_c = 0, _pj_a = util.range(trials.length), _pj_b = _pj_a.length; (_pj_c < _pj_b); _pj_c += 1) {
            row = _pj_a[_pj_c];
        for (var row, _pj_c = 0, _pj_a = util.range((trials.length - 1)), _pj_b = _pj_a.length; (_pj_c < _pj_b); _pj_c += 1) {
            row = _pj_a[_pj_c];
            if ((trials[row]["stimuliCondition"] == trials[(row + 1)]["stimuliCondition"])) {
                conditionMet = false;
            } else {
                conditionMet = true;
    customOrder = [];
    for (var row, _pj_c = 0, _pj_a = util.range(trials.length), _pj_b = _pj_a.length; (_pj_c < _pj_b); _pj_c += 1) {
        row = _pj_a[_pj_c];
    return customOrder;

Thank you,