fredvbrug / STOP-IT

19 stars 37 forks source link

Advice for adapting to an fMRI paradigm #1

Open jaredpz opened 5 years ago

jaredpz commented 5 years ago

I'd like to use this version of the stop-signal in an fMRI paradigm, but I'm not sure about the best way to modify it for these purposes.

I think the easiest way to do this would probably to have a large number of short blocks and to have the inter-block interval var bFBT jittered. It would also be necessary to have the blocks start automatically after the inter-block interval instead of require a key-press.

Any advice on making these edits?

luc-vermeylen commented 5 years ago

Hi Jared,

To eliminate the key-press before starting a block, you could remove block_start from line 523 in experiment.html:

timeline: [block_start, block_get_ready, trial_procedure, block_feedback],

so that it becomes:

timeline: [block_get_ready, trial_procedure, block_feedback],

I am not sure why you would like to have the inter-block interval jittered? I suppose you that actually want the inter-trial interval to be jittered for fMRI, no?

To jitter the inter-trial interval you could change the ITI variable in the configuration.js file to a vector of ITI durations (line 54):

var ITI = [500 600 700 800 900 1000]; // ITI will be chosen at random between these values

Next, you can change the trial_duration of the blank_ITI trial object in the experiment.html file on line 252 to the following:

trial_duration: function(){ return jsPsych.randomization.sampleWithoutReplacement(ITI, 1)[0]; } }

Nevertheless, the most difficult part will be getting jspsych to communicate with the scanner. The scanner's pulse should trigger the start of a block (or multiple blocks) so that you could figure out the onsets of the events that you are interested in (i.e., so all events have a timing relative to the first scanning pulse). Therefore, jspsych and your browser will need to receive a trigger by serial or parallel port. I have no experience with getting this to work, but there exists an extension to do this (https://github.com/rivasd/jsPsychHardware). I guess it might be somewhat tedious to get this to work, but perhaps your fMRI technician could help you with this? Anyway, i would be curious if you got this to work!

I hope this already helps you somewhat!

Best, Luc

ahmetc36 commented 4 years ago

Hi - was anyone able to successfully implement the jsPsychHardware fix? Would it be a better idea to modify this task to be scanner friendly using the Matlab/PTB version on https://osf.io/3swfm/ despite the disclaimer on there?

Thanks! Ahmet

luc-vermeylen commented 4 years ago

Hi ahmetc36,

Robert Zenz from Sport- und Bewegungswissenschaft der Universität Salzburg (with this information you will be able to find his email) was working on an EEG version of the STOP-IT (using serial port triggers). Last i heard, he had a working version!

I would contact him to get his version and feedback on using the jsPsychHardware plugin. If you would be able to create a scanner compatible version of the STOP-IT, it would be very nice if you could report your experience back here and maybe we could then share this version on github as well.

Best, Luc Vermeylen

ahmetc36 commented 4 years ago

Hi Luc,

I wasn't able to talk to Robert Zenz about this, but I believe the scanner trigger issue may be solved in a simpler way than integrating jsPsychHardware. For example, after the screen maximizes and the instructions are shown, a slide awaits the space button to be pressed to begin the experiment. At least for our scanner, if I change the allowable response from "space" to one that the scanner sends as a trigger (in our case, that would be "="), I should be able to get the scanner to start the experiment. This is the way it works in eprime, and our MRI tech folk seem to agree that a simple response collection edit should do the trick. In fact, at the end of the block there is another slide that collects "space", and this could be changed as well to start a new run in the scanner.

As for timing, it looks as though the "time_elapsed" variable logs the trial time since the html file is generated (but not since the spacebar is pressed). In order to make the timing in sync with the scanner, if the button press at the "press space to continue" slide was logged as a variable or as an additional row, the scanner-synced time could be derived by subtracting (slide onset + RT) from the time_elapsed row.

Is there a way to do the above in this script? I've been reading through the experiment.html file, but my inexperience with jsPsych shows!

Thanks for all your help! Ahmet

luc-vermeylen commented 4 years ago

Hi Ahmet,

That's indeed an easy way to solve the scanner trigger issue!

Regarding the timing of the block onset, i can show you an easy way to get this information in the data file. I will assume you will be using the locally saved .csv file (the one that downloads in the browser).

The point in the script were the offline saving happens starts at line 434. Here, specific rows and columns are selected to be saved in the final datafile. But first, we have to make sure that the block_start event has some unique identifier, so we can select it to be included in the datafile. So starting around line 233 you could add an on_finish to the event like this:

`

var block_start = { type: 'html-keyboard-response', stimulus: text_at_start_block, choices: ['space'], on_finish: function(data) { data.block_start = true } };

`

Next, change the event that saves the data (starting at line 434). You only have to change the variable var rows to include the block_start: true in addition to the trial_type: 'custom-stop-signal-plugin'

` // end trial and save the data var goodbye = {

type: "html-keyboard-response",

stimulus: end_message,

on_start: function(data) {
  var subjID = jsPsych.data.get().last(1).values()[0]['participantID'];
  var full_data = jsPsych.data.get();
  var ignore_columns = ['raw_rt','trial_type','first_stimulus','second_stimulus','onset_of_first_stimulus',
    'onset_of_second_stimulus','key_press','correct_response','trial_index','internal_node_id'];
  var rows = [{trial_type: 'custom-stop-signal-plugin'}, {block_start: true}]; // so, we will log stop signal trials and block_start events only
  var selected_data = jsPsych.data.get().filter(rows).ignore(ignore_columns);

`

Now the datafile will have a mostly empty row with a value for time elapsed and rt that show the onset of the block! Let me know if something is not clear or not working!

Best, Luc

ahmetc36 commented 4 years ago

Hi Luc,

My apologies for the criminally long delay in responding. Your edits were very helpful. I am now able to extract the time at which the task receives the scanner input.

I also needed to jitter the ITI as asked above, and followed your instructions: in the experiment_variables.js file: var ITI = [1500, 2000, 2500, 3000, 3500, 4000]; // ITI will be chosen at random between these values in the experiment.html file: trial_duration: function(){ return jsPsych.randomization.sampleWithoutReplacement(ITI, 1)[0];},

I would imagine this to sample from the var ITI values randomly, but when I run the experiment, it seems like the ITIs are unexpectedly long (definitely longer than the maximum of 4 seconds). Why might this be happening? Maybe the ITI is looping 2-3 times before moving on to the next trial?

Lastly, could I eliminate the practice block altogether? It looks like I'm able to minimize it to a single trial when I alter it to read:

var NdesignReps_practice = 0

but it would be ideal if I could make a version of it that jumps straight into the actual task.

Thanks for all your help! Ahmet

luc-vermeylen commented 4 years ago

Hi Ahmet,

I would also like to apologize for this criminally late response! I forgot i still had to respond to this one!

For the ITI you could try to replace:

return jsPsych.randomization.sampleWithoutReplacement(ITI, 1)[0];},

with:

return jsPsych.randomization.sampleWithoutReplacement(ITI/2, 1)[0];},

In the original version we also halved the ITI when setting the actual duration, so you probably should keep on doing that here! I don't actually remember why we did it, but it should function just fine.

To skip the practice block you could also change block_ind variable initalization from 0 to 1 on line 39 in the experiment.html.

var block_ind = 1;

Hope this works, Luc

ahmetc36 commented 4 years ago

Hi Luc,

Sorry again for taking so long to update! Dividing the ITI variable by 2 like you suggested didn't work on my end, but altering the script such that the FIX variable contained my ITI range instead ended up working:

fixation: jsPsych.timelineVariable('fixation'), fixation_duration: function(){ return jsPsych.randomization.sampleWithoutReplacement(FIX, 1)[0]; },

Where the FIX variable in experiment_variables.js now contains the possible ITI durations (intended to range from 1500 to 4000):

var FIX = [1500, 2000, 2500, 3000, 3500, 4000];

I wonder if this is because var/2 is not accepted syntax, or if "ITI" is a restricted variable name (I did have more luck manipulating the ITI variable when I altered its name to ITIDUR). In any case, it seemed to perform as intended when I chose FIX as the jittering event. I also made the blank_ITI trial_duration: 0 in experiment.html so that FIX would be the only trial separator:

` var blank_ITI = {

type: 'jspsych-detect-held-down-keys',

// this enables the detection of held down keys stimulus: "+", // changed to + for visibility during debugging trial_duration: 0,

response_ends_trial: false,

}; `

This means the fixation image alone serves as a jittering ITI (which seems ideal for fMRI).

The scanner trigger slide is also logging the elapsed response time properly in the data output. I can use this value to calculate each experiment event while accounting for the scanner trigger. However, although it make sense to calculate "elapsed time at which the scanner trigger is received" for the scanner trigger slide, I'm realizing that the rest of the "elapsed_time" column is also logging the "elapsed time at which subject responds to stimulus". This is great and all, but it would be very useful to also log the stimulus (i.e., arrow) onset time in each trial. I tried going through the jspsych plugins/examples and the jspsych js file, but did not find an example/method of accomplishing this.

I also tried logging the jittering ITI (now contained in "FIX") at each trial, just for peace of mind, but my efforts didn't yield anything useful here either (just seeing a blank column...).

Do you have any suggestions for logging arrow onset and the randomly selected fixation duration at each trial? I attached my working experiment.html + text_variables and experiment_variables files here it helps. I would also be happy to share these scripts again when these issues are resolved, in case anyone else ends up needing an fMRI-ready version of your task: fmri_sst.zip

Thanks for all your help! Ahmet

luc-vermeylen commented 4 years ago

Hi Ahmet,

Great to see that you found a workaround for the ITI in the end!

For your question concerning the fixation and stimulus onset, i made some changes to the custom-stop-signal-plugin.js and the experiment.html that allows the logging of these onset times in the datafile! I attached my version of the task, and commented all my changes with my name ("luc"), so you can easily find what i changed (the changes are only in the custom plugin in the js folder and the main experiment.html). Hope this also works on your setup!

Very nice that you want to share the fMRI version when you are ready :-).

Good luck! Luc STOP-IT-ahmetc36.zip

ahmetc36 commented 4 years ago

Luc,

This is amazing! Thank you so much for taking the time. The scanner trigger, arrow onset, RT, fixation onset, and the fixation duration are all logged, so I think we're in good shape! If I ever need the onset time for the stop signal, I can just add the SSD to the arrow onset..

I'm going to enlarge the images in the task a bit for better visibility when this is carried to the scanner, but other than that I think this is all ready. I'll upload a zip here once that's done.

Thanks again for all the help! :)

Ahmet

ahmetc36 commented 4 years ago

Hi - as far as I can tell, this is ready for the fMRI setting! SST_fMRI.zip

While test running this I ran into a dilemma. Now that there are no "blank_ITI" slides, and the jitter is handled by the fixation only, any accidental response registered during the fixation slide is logged as incorrect and the script kicks to the next trial's fixation. This is probably a good thing since we don't want participants to prematurely respond and try to beat the arrow onset. However, it does mean that someone can essentially mash the key at the fixation slide to jump from trial to trial and reach the end of the experiment as quickly as possible.

In the original version, the blank_ITI would guard against this, though repeated pressing during the blank slide would indefinitely delay the next event (e.g., fixation) onset (since the loop is only terminated when there is no response during blank_ITI).

I'm unsure if leaving it as is (presses logged as incorrect at fixation, jump to next trial), delaying the arrow onset (button presses during fixation prevent arrow onset), or simply not logging responses during fixation would be best. I'm inclining towards the last one, but would love to hear your thoughts!

Best, Ahmet

luc-vermeylen commented 4 years ago

Hi Ahmet,

Given the context of an fMRI scanning session, i don't think it will be too plausible that a participant will just mash the keys to skip trials as the participant is probably (1) quite motivated to do the task well, (2) knows that the experimenter is looking and can evaluate performance live and (3) participants can be instructed/trained very well on the task before entering the scanner (all of this compared to an online setting).

In addition, (accidental) premature responses are also useful for SSRT estimation using the integration method (see Verbruggen et al., 2019, Elife). So, i would instruct participants well on the task (and the possibility to prematurely respond) so they don't willingly respond prematurely, but keep logging accidental premature responses as they are useful for SSRT estimation.

Good luck with the study! Luc

ahmetc36 commented 4 years ago

Hi Luc,

Very valid points! I think proper training before the scan can help prevent this for the most part. I tested a session where I made premature go responses during the fixation in each trial, and looks like this type of incorrect response can indeed be distinguished in the data output. For instance, a premature rightarrow response will be marked as incorrect even if stim=right and signal=no. I see that we can also estimate the RT at which the premature response was made by subtracting the "onset of first stimulus" column (which logs the randomly chosen fixation duration in my version) from the negative RT value (e.g., RT is logged as -2875 for a 3000 ms fixation, meaning the response was logged 125ms into the fixation onset). This info is probably necessary when one might need to compare unsuccessful stop RT with Go RT as Verbruggen et al. 2019 suggests, since it's recommended to include RT from these premature responses.

Thanks again for all your help! Ahmet