Trial objects

Trial objects are optional. For each trial, you only need to make one if you want to change a property from default. Note you can also change some default values using pb_prefs.

An object of type trial allows some options for a whole trial, e.g. pre-trial interval (default = 0.75 sec). For each trial you define in an experiment script, you can make a trial object with trialObject and include it with element objects in an input to addTrial.

Trial timing

In a PsychBench experiment script use function addTrial to define trials in memory. You only need to define each distinct trial once (each distinct combination of property values across all objects in a trial), and in any convenient order. Then set trial repetition and order using function setTrialList. Alternatively in unusual cases you can set it in the trial definitions themselves using trial object property n. See Building and running an experiment.

By default each trial ends when no elements in it are left running or waiting to start at definite times. The next trial then automatically starts after an interval which you can change in trial object property preTrialInterval (default = 0.75 sec). In this mode trial start and end times are flexible, which is okay for most experiments.

Alternatively you can set trials to start at fixed times from a trigger in a past trial using trial object property start. Typically do this in scanner experiments.

Input properties

pretrialInterval

Default: 0.75 sec

preTrialInterval sets time from previous trial end to this trial start (sec). By default the pre-trial interval is just a blank screen. You can change its background color using preTrialBackColor below. You also can show a static visual element in it by setting the element’s start field preTrial = <cd>true<cd>. The most common example is a cross object for a fixation cross. See also record property preTrialInterval_r.

Sufficient pre-trial interval

PsychBench always needs some time in each pre-trial interval to close the previous trial and prepare this one. How much time depends on what elements you run and on your system. If preTrialInterval is too short, PsychBench will extend the interval to the minimum length it needs. By default the experiment time course will just pause at that point, which is okay in most experiments. The exception is if you use start.timeFromTrigger below to set a fixed start time—see there if you need more information.

If PsychBench extends a pre-trial interval it will show a warning after the experiment with how much longer the interval needed to be. You can use these warnings to set preTrialInterval longer if needed. You can also include record property preTrialHeadroom below in experiment results output.

Another reason pre-trial interval can be longer than expected is start latency in an element at trial start. PsychBench will not warn about this. If precise timing is important, you can check for this by including element record property startLatencyBufferable in results and fix using element property startBuffer.

preTrialBackColor

Default: same as trial

A 1×3 RGB vector with numbers between 0–1 setting background color for the pre-trial interval. Default is same as trial, which you can set in backColor below.

backColor

Default: same as experiment

A 1×3 RGB vector with numbers between 0–1 setting background color for the trial. Default is same as experiment, which you can set in experiment object property backColor.​

Note if you need to set background color for only part of a trial, use a rectangle element with property size = <cd>inf<cd> instead.

start

Default: trial starts flexibly whenever previous trial ends + pre-trial interval

If you set start, the trial starts at a fixed time relative to a trigger in a past trial. For all elements in the trial, element start/end field t (time from trial start) is then relative to that expected trial start time, which makes them effectively relative to the trigger. Using trial start is essential to prevent timing drift in experiments that run multiple (or all) trials from a trigger, e.g. in a scanner experiment. (If you re-trigger in each trial, you don’t need it—just set elements in each trial to start/end from trigger directly using element start/end fields trigger/triggerBy.)

First use an element in one trial to register a trigger, and set element property startTrialsFromTrigger = <cd>true<cd>. Then for any number of successive trials, set trial object property start to fix start times relative to the trigger. start is a further struct with one field start.timeFromTrigger containing a 1×2 vector <cd>[t0 dt]<cd>. This works incrementally, so generally you can set it the same for all successive trials:

  1. <cd>t0<cd> = start time relative to trigger if this is the first trial after it (sec)
  2. <cd>dt<cd> = start time increment if this is a later trial (sec)

Setting start.timeFromTrigger across trials does three things:

  1. Each trial’s start time is fixed relative to trigger so small timing deviations at the trial level won’t accumulate across trials to cause drift.
  2. If a trial ends early (e.g. subject responds fast) then the next trial will wait to start on time, extending its pre-trial interval backward.
  3. If a trial would run late (e.g. subject doesn’t respond in time) then it will end so the next trial starts on time, including allowing for the next trial’s pre-trial interval.

You can trigger once for the whole experiment or re-trigger in any number of trials. If a trial has start.timeFromTrigger set but there is no trigger to start trials from yet (or any trial since the last trigger started flexibly), the trial ignores it and starts flexibly. This can be useful sometimes—see example below.

Example 1. Equal trial start intervals

This experiment script defines one trial that runs a keyPress element that waits for a trigger signal. It then defines 20 test trials with the same setting for start.timeFromTrigger. The experiment runs one block of the sync trial + the 20 test trials in random order, then a second similar block. i.e. it waits to sync once at experiment start, then again half way through the experiment. In each block, whichever test trial runs first starts 0.75 sec after the trigger, then the others start at 10 sec increments. So trial start times in each block aim for 0.75, 10.75, 20.75, 30.75, … sec from trigger.

Note we also set start.timeFromTrigger for the sync trial. This is just for when it runs in the middle of the experiment. When it runs at start, the experiment will not be synced yet, so it will just ignore this property (we left its first number = 0 as a reminder).

newExperiment


<cdcm>% -<cdcm>
syncer = keyPressObject;
syncer.registerTrigger = true;
syncer.startTrialsFromTrigger = true;
syncer.start.t = 0;
<cdcm>% keyPress ends on its own when it registers a trigger<cdcm>
trial = trialObject;
trial.start.timeFromTrigger = [0 10];

addTrial(syncer, trial, <cdsm>"sync"<cdsm>);
<cdcm>% -<cdcm>


<cdkm>for<cdkm> n = 1:20
    <rm><Make element objects for trial><rm>

    trial = trialObject;
    trial.start.timeFromTrigger = [0.75 10];

    addTrial(<rm><element objects><rm>, trial);
<cdkm>end<cdkm>


nn = [<cdsm>"sync"<cdsm> randomOrder(1:20) <cdsm>"sync"<cdsm> randomOrder(1:20)];
setTrialList(nn)

[results, resultsMatrix] = runExperiment;

Example 2. Jittered trial start intervals

In fMRI experiments it’s common to randomize inter-trial intervals so that stimulus onset is sampled at different points in the scanner cycle. You can do this by adding a random offset to trial start.timeFromTrigger. As one example, modify the test trial definition loop above by adding a random offset sampled without replacement from 1, 2, 3, 4 sec. Randomization is implemented in the trial order. Each offset appears 5 times in a test block, so the last trial in a block will always start at the same time relative to the block’s trigger (0.75 + 19×10 + 5×1 + 5×2 + 5×3 + 5×4 = 240.75 sec). We make sure to report trial start times in experiment results output.

offsets = replicate([1 2 3 4], 5);
<cdkm>for<cdkm> n = 1:20
    offset = offsets(n);

    <rm><Make element objects for trial><rm>

    trial = trialObject;
    trial.start.timeFromTrigger = [0.75 10]+offset;
    trial.report = <cdsm>"startTime"<cdsm>;

    addTrial(<rm><element objects><rm>, trial);
<cdkm>end<cdkm>


nn = [<cdsm>"sync"<cdsm> randomOrder(1:20) <cdsm>"sync"<cdsm> randomOrder(1:20)];
setTrialList(nn)

Timing precision

If you set trial start.timeFromTrigger for a trial, it’s important that pre-trial interval is long enough for the processing PsychBench needs to do between trials. If it isn’t, the trial will still need to start late. See preTrialInterval above for more information.

More generally, precise timing is often important in synchronized experiments. If an element starts or ends late, or the trial starts late due to an element starting late at trial start, you can check element record properties startLatencyBufferable/endLatencyBufferable in experiment results output and fix using element properties startBuffer/endBuffer.

withStaircase

Default: automatic if trial contains staircased elements and the experiment contains only one staircase

Usually you can leave withStaircase at default, including in most staircase experiments with only one staircase.

If you’re running an experiment with more than one staircase, for each trial that contains staircased elements, you must set withStaircase to specify which staircase to use for the trial.

You can also set withStaircase for a trial if it doesn’t contain staircased elements but you want to associate it with a staircase so that it will be skipped if it would run after the staircase ends. The trial will not actually be in the staircase, e.g. the staircase will not update its value or threshold estimate after the trial.

withStaircase is a string pointing to a staircase object by variable name and possibly index you use in the experiment script. e.g. <cds>"staircase"<cds>, <cds>"staircases(2)"<cds>, etc. Each trial can only be in (or associated with) one staircase. Tip: If you have an index in a numeric variable you can use it in an <cds>"x"<cds> string (but not an <cds>'x'<cds> string) like this: <cds>"pictures("<cds><cd> + n + <cd><cds>")"<cds>

n

Default: set trial repetition and order using command setTrialList

Usually ignore this property and set trial repetition and order with the command setTrialList. See Building and running an experiment.

In unusual cases you may want to set repetition and order in the trial definitions themselves. You can do that by making trial objects and setting this property. This is just a number 1, 2, 3, ... specifying where in the order the trial definition will run. To repeat the trial definition, set n to a row vector specifying multiple points in the order. Note you can’t mix using a trial list and n—if you call setTrialList, it overrides any n‘s you set.

Input properties all objects have

report
info

Record properties

PsychBench uses record properties to record information during experiments. You can't set record properties but you can see them in experiment results using input property report.

n_def
n

n_def is trial definition ID, before any repetition/ordering. By default PsychBench numbers trial definitions 1, 2, 3, … as addTrial is called in the experiment script. You can also use custom numbers or strings (see addTrial function help).

n is actual trial number during the experiment, after any repetition/ordering. Usual set repetition/ordering by the command setTrialList, or in unusual cases by setting n‘s directly (above).

preTrialInterval_r
preTrialHeadroom

preTrialInterval_r records time from previous trial end to this trial start (sec). Note this property records the actual interval during the experiment—to set interval, use input property preTrialInterval above.

preTrialHeadroom is time left in the set pre-trial interval when PsychBench completed the processing it needed to do between the trials (sec). < 0 means preTrialInterval was set too short and the interval extended.

startTime
endTime
duration

startTime and endTime record start/end times relative to trial 1 start (sec). These = offset of inter-trial interval at trial start and onset of inter-trial interval at trial end, both at screen refresh. If the trial starts/ends with visual stimuli, these also = those stimulus onset/offset times.

duration records end time − start time.

frameIntervals

frameIntervals is a row vector of durations of all frames in the trial (sec), indexed by frame number. This can be used with element record properties n_startFrame and n_endFrame. For example, you can see a dropped frame at an element start by checking for an extended interval at the frame before n_startFrame, or at end at frame n_endFrame. You can also get a general sense of the rate across frames. For more information on frames see Timing precision.