Real-time fMRI-triggered experience sampling: A proof-of-concept study.
The 5 matches · 1 of them tie a paragraph to a whole file, not to given lines: a weak match, whose lines are not tinted
- [1] § Results › Exploratory validation of real-time BOLD signal triggers ↔ Code/Figure 3/offlineonlineplots.m, lines 680–758 · score 0.68 · Shaded error bars, PMC activity, PMC Trigger Events, trial onset, BOLD activity, SEM
- [2] § Results › Description of final dataset ↔ Code/Figure 2/exclusionbarplot.m, lines 33–61 · score 0.62 · Opposite ROI triggered, Incomplete rating, Head Motion, thoughts, Figure 2
- [3] § Results › Exploratory validation of real-time BOLD signal triggers ↔ Code/Figure 3/offlineonlineplots.m, lines 320–397 · score 0.61 · offline PMC, trial onset, BOLD activity, trigger events, alignment, validation
- [4] § Data Analyses › Power analysis ↔ Code/Figure 4 + Pre-registered Hypothesis Testing/rtfMRI_HypothesisTesting.R, lines 44–122 · score 0.59 · fitted model, lme4, CIs, coefficient, slope
- [5] § Methods › Experimental tasks › GradCPT ↔ gradCPT_mk_Mt_EV.m, the whole file · a weak match · score 0.56 · button presses, commission, city, mountains, stimuli, event
Paper
Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC
The paper is loaded when this pane is shown.
The authors' code
MATLAB · 758 lines · 33 KB · no license · 2 matches
- %% Offline Online Comparison
- %Written July 25th 2025 by Tiara Bounyarith
- %% Group level and / or subject level analysis
- group_level = 1;
- subject_level = 0;
- %%
- data_dir = '/Volumes/LaCie/R21-rtfMRI';
- %Load in rtfMRI master table
- load([data_dir filesep 'analyses' filesep 'rtfMRI_master_table.mat']);
- %Exclude trials
- master = master(master.keep, :);
- %Exclude subjects
- excludedsubs = ["sub-01", "sub-02", "sub-28", "sub-29", "sub-30", "sub-44", "sub-70"];
- master = master(~ismember(master.subj, excludedsubs), :);
- %Get list of remaining subjects
- subs = unique(master.subj');
- %epoch range
- epoch_range = 8;
- %make plot folder for group
- if group_level == 1
- mkdir([data_dir filesep 'analyses' filesep 'OfflineOnlineCompare' filesep 'group_level_plots']);
- savepath_group=[data_dir filesep 'analyses' filesep 'OfflineOnlineCompare' filesep 'group_level_plots'];
- end
- %make plot folder for subj
- if subject_level == 1
- mkdir([data_dir filesep 'analyses' filesep 'OfflineOnlineCompare' filesep 'subject_level_plots']);
- savepath_sub=[data_dir filesep 'analyses' filesep 'OfflineOnlineCompare' filesep 'subject_level_plots'];
- end
- %% Graph activity around when daIC trials were triggered
- if group_level == 1
- group_online_target_daIC_epochs = []; group_z_online_target_daIC_epochs = [];
- group_online_nontarget_PMC_epochs = []; group_z_online_nontarget_PMC_epochs = [];
- group_offline_target_daIC_epochs = []; group_z_offline_target_daIC_epochs = [];
- group_offline_nontarget_PMC_epochs = []; group_z_offline_nontarget_PMC_epochs = [];
- end
- for i=1:length(subs)
- runs = length(unique(master.run(master.subj == subs(i))));
- subj_num = str2double(extractAfter(subs(i), "sub-"));
- if subject_level == 1
- sub_online_target_daIC_epochs = []; sub_z_online_target_daIC_epochs = [];
- if subj_num > 20
- sub_online_nontarget_PMC_epochs = [];
- sub_z_online_nontarget_PMC_epochs = [];
- end
- sub_offline_target_daIC_epochs = []; sub_z_offline_target_daIC_epochs = [];
- sub_offline_nontarget_PMC_epochs = []; sub_z_offline_nontarget_PMC_epochs = [];
- end
- for j=1:runs
- target_ROI = unique(master.ROI(master.subj == subs(i) & master.run == j));
- % ensure we are only looking at runs that are triggering from the daIC
- if target_ROI == "daIC"
- nontarget_ROI = "PMC";
- %load in online target daIC data
- load([data_dir filesep 'subject_opennft_data' filesep char(subs(i)) filesep 'mainLoopData' filesep char(subs(i)) '_' num2str(j) '_mainLoopData.mat'], 'scalProcTimeSeries');
- online_target_daIC = scalProcTimeSeries(1,:);
- %load in online nontarget PMC data for subjects post-algorithm fix
- if subj_num > 20
- online_nontarget_PMC = scalProcTimeSeries(2,:);
- end
- %load in offline target daIC data
- subj_padded = sprintf('sub-%03d', subj_num);
- offline_target_daIC = (load([data_dir filesep 'analyses' filesep 'OfflineOnlineCompare' filesep 'OfflineTargetROITimeSeries' filesep subj_padded filesep 'run' num2str(j) filesep subj_padded '_run' num2str(j) '_preproc_meants.txt']))';
- %load in offline nontarget PMC data
- offline_nontarget_PMC = (load([data_dir filesep 'analyses' filesep 'OfflineOnlineCompare' filesep 'OfflineNontargetROITimeSeries' filesep subj_padded filesep 'run' num2str(j) filesep subj_padded '_run' num2str(j) '_preproc_meants.txt']))';
- trials = master.trial_num(master.subj == subs(i) & master.run == j);
- for k=1:length(trials)
- % ensure we're only looking at daIC-triggered trials
- if master.trigger_type(master.subj == subs(i) & master.run == j & master.trial_num == trials(k)) == "ROI"
- % epoch around ROI-triggered trials for this run - online
- % target daIC
- curr_trial = master.trial_onset_TR(master.subj == subs(i) & master.run == j & master.trial_num == trials(k));
- run_length = size(online_target_daIC,2);
- start_epoch = max(1, curr_trial - epoch_range);
- end_epoch = min(run_length, curr_trial + epoch_range);
- curr_online_tar_daIC_epoch = online_target_daIC(1, start_epoch:end_epoch);
- if group_level == 1
- group_online_target_daIC_epochs = [group_online_target_daIC_epochs; curr_online_tar_daIC_epoch];
- end
- if subject_level == 1
- sub_online_target_daIC_epochs = [sub_online_target_daIC_epochs; curr_online_tar_daIC_epoch];
- end
- curr_z_online_tar_daIC = (curr_online_tar_daIC_epoch - mean(curr_online_tar_daIC_epoch)) / std(curr_online_tar_daIC_epoch);
- if group_level == 1
- group_z_online_target_daIC_epochs = [group_z_online_target_daIC_epochs; curr_z_online_tar_daIC];
- end
- if subject_level == 1
- sub_z_online_target_daIC_epochs = [sub_z_online_target_daIC_epochs; curr_z_online_tar_daIC];
- end
- % epoch around ROI-triggered trials for this run - online
- % nontarget PMC
- if subj_num > 20
- run_length = size(online_nontarget_PMC,2);
- start_epoch = max(1, curr_trial - epoch_range);
- end_epoch = min(run_length, curr_trial + epoch_range);
- curr_online_nontar_PMC_epoch = online_nontarget_PMC(1, start_epoch:end_epoch);
- if group_level == 1
- group_online_nontarget_PMC_epochs = [group_online_nontarget_PMC_epochs; curr_online_nontar_PMC_epoch];
- end
- if subject_level == 1
- sub_online_nontarget_PMC_epochs = [sub_online_nontarget_PMC_epochs; curr_online_nontar_PMC_epoch];
- end
- curr_z_online_nontar_PMC = (curr_online_nontar_PMC_epoch - mean(curr_online_nontar_PMC_epoch)) / std(curr_online_nontar_PMC_epoch);
- if group_level == 1
- group_z_online_nontarget_PMC_epochs = [group_z_online_nontarget_PMC_epochs; curr_z_online_nontar_PMC];
- end
- if subject_level == 1
- sub_z_online_nontarget_PMC_epochs = [sub_z_online_nontarget_PMC_epochs; curr_z_online_nontar_PMC];
- end
- end
- % offline target daIC
- run_length = size(offline_target_daIC,2);
- start_epoch = max(1, curr_trial - epoch_range);
- end_epoch = min(run_length, curr_trial + epoch_range);
- curr_offline_tar_daIC_epoch = offline_target_daIC(1, start_epoch:end_epoch);
- if group_level == 1
- group_offline_target_daIC_epochs = [group_offline_target_daIC_epochs; curr_offline_tar_daIC_epoch];
- end
- if subject_level == 1
- sub_offline_target_daIC_epochs = [sub_offline_target_daIC_epochs; curr_offline_tar_daIC_epoch];
- end
- curr_z_offline_tar_daIC = (curr_offline_tar_daIC_epoch - mean(curr_offline_tar_daIC_epoch)) / std(curr_offline_tar_daIC_epoch);
- if group_level == 1
- group_z_offline_target_daIC_epochs = [group_z_offline_target_daIC_epochs; curr_z_offline_tar_daIC];
- end
- if subject_level == 1
- sub_z_offline_target_daIC_epochs = [sub_z_offline_target_daIC_epochs; curr_z_offline_tar_daIC];
- end
- % offline nontarget PMC
- run_length = size(offline_nontarget_PMC,2);
- start_epoch = max(1, curr_trial - epoch_range);
- end_epoch = min(run_length, curr_trial + epoch_range);
- curr_offline_nontar_PMC_epoch = offline_nontarget_PMC(1, start_epoch:end_epoch);
- if group_level == 1
- group_offline_nontarget_PMC_epochs = [group_offline_nontarget_PMC_epochs; curr_offline_nontar_PMC_epoch];
- end
- if subject_level == 1
- sub_offline_nontarget_PMC_epochs = [sub_offline_nontarget_PMC_epochs; curr_offline_nontar_PMC_epoch];
- end
- curr_z_offline_nontar_PMC = (curr_offline_nontar_PMC_epoch - mean(curr_offline_nontar_PMC_epoch)) / std(curr_offline_nontar_PMC_epoch);
- if group_level == 1
- group_z_offline_nontarget_PMC_epochs = [group_z_offline_nontarget_PMC_epochs; curr_z_offline_nontar_PMC];
- end
- if subject_level == 1
- sub_z_offline_nontarget_PMC_epochs = [sub_z_offline_nontarget_PMC_epochs; curr_z_offline_nontar_PMC];
- end
- end
- end
- elseif target_ROI == "PMC"
- nontarget_ROI = "daIC";
- end
- end
- if subject_level == 1
- % find grand mean & SEM of z-scored online target daIC activity
- sub_avg_online_tar_daIC_epoch = mean(sub_z_online_target_daIC_epochs,1);
- sub_sem_online_tar_daIC_epoch = std(sub_z_online_target_daIC_epochs, 0, 1) / sqrt(size(sub_z_online_target_daIC_epochs,1));
- % mean & SEM of z-scored online nontarget PMC
- if subj_num > 20
- sub_avg_online_nontar_PMC_epoch = mean(sub_z_online_nontarget_PMC_epochs,1);
- sub_sem_online_nontar_PMC_epoch = std(sub_z_online_nontarget_PMC_epochs, 0, 1) / sqrt(size(sub_z_online_nontarget_PMC_epochs,1));
- end
- % mean & SEM z-scored offline daIC
- sub_avg_offline_tar_daIC_epoch = mean(sub_z_offline_target_daIC_epochs,1);
- sub_sem_offline_tar_daIC_epoch = std(sub_z_offline_target_daIC_epochs, 0, 1) / sqrt(size(sub_z_offline_target_daIC_epochs, 1));
- % mean & SEM z-scoredoffline PMC
- sub_avg_offline_nontar_PMC_epoch = mean(sub_z_offline_nontarget_PMC_epochs,1);
- sub_sem_offline_nontar_PMC_epoch = std(sub_z_offline_nontarget_PMC_epochs, 0, 1) / sqrt(size(sub_z_offline_nontarget_PMC_epochs,1));
- % Graph
- figure('Position', [500 500 1200 500])
- %figure('Position', [900 500 1500 500])
- a = shadedErrorBar([], sub_avg_online_tar_daIC_epoch, sub_sem_online_tar_daIC_epoch, 'lineprops', {'color', [1, 0, 0]}, 'patchSaturation', 0.08);
- hold on
- if subj_num > 20
- b = shadedErrorBar([], sub_avg_online_nontar_PMC_epoch, sub_sem_online_nontar_PMC_epoch, 'lineprops', {'color',[0, 0, 1]}, 'patchSaturation', 0.08);
- c = shadedErrorBar([], sub_avg_offline_tar_daIC_epoch, sub_sem_offline_tar_daIC_epoch, 'lineprops', {'color',[0.6, 0, 0]}, 'patchSaturation', 0.08);
- d = shadedErrorBar([], sub_avg_offline_nontar_PMC_epoch, sub_sem_offline_nontar_PMC_epoch, 'lineprops', {'color',[0, 0, 0.6]}, 'patchSaturation', 0.08);
- else
- b = shadedErrorBar([], sub_avg_offline_tar_daIC_epoch, sub_sem_offline_tar_daIC_epoch, 'lineprops', {'color',[0.6, 0, 0]}, 'patchSaturation', 0.08);
- c = shadedErrorBar([], sub_avg_offline_nontar_PMC_epoch, sub_sem_offline_nontar_PMC_epoch, 'lineprops', {'color',[0, 0, 0.6]}, 'patchSaturation', 0.08);
- end
- if subj_num > 20
- set(a.edge, 'LineWidth', 2, 'LineStyle', ':')
- a.mainLine.LineWidth = 4;
- set(b.edge, 'LineWidth', 2, 'LineStyle', ':')
- b.mainLine.LineWidth = 4;
- set(c.edge, 'LineWidth', 2, 'LineStyle', ':')
- c.mainLine.LineWidth = 4;
- set(d.edge, 'LineWidth', 2, 'LineStyle', ':')
- d.mainLine.LineWidth = 4;
- else
- set(a.edge, 'LineWidth', 2, 'LineStyle', ':')
- a.mainLine.LineWidth = 4;
- set(b.edge, 'LineWidth', 2, 'LineStyle', ':')
- b.mainLine.LineWidth = 4;
- set(c.edge, 'LineWidth', 2, 'LineStyle', ':')
- c.mainLine.LineWidth = 4;
- end
- xline(epoch_range+1, '_', {'Trial Onset'}, 'LabelVerticalAlignment','bottom','LabelHorizontalAlignment','center','FontSize', 18, 'LineWidth', 2)
- xlabel('TR', 'FontSize', 20)
- xticks([1:17])
- xticklabels({'-8','-7','-6','-5', '-4', '-3', '-2', '-1', '0', '1', '2', '3', '4', '5', '6','7','8'})
- ax=gca;
- ax.XAxis.FontSize = 25;
- % ylim([-1.5 2.0]);
- ylabel('BOLD Activity (z)', 'FontSize', 20)
- ax.YAxis.FontSize = 20;
- if subj_num > 20
- legend({'Real-time daIC Activity (Target)', 'Real-time PMC Activity (Non-target)', 'Offline daIC Activity (Target)', 'Offline PMC Activity (Non-target)'}, 'FontSize', 18, 'Location', 'best');
- else
- legend({'Real-time daIC Activity (Target)', 'Offline daIC Activity (Target)', 'Offline PMC Activity (Non-target)'}, 'FontSize', 18, 'Location', 'best');
- end
- title({char(subs(i)),'Real-time vs Offline Estimates of Average Region Activity During daIC-triggered Trials'}, 'FontSize', 23);
- if subj_num <20
- subtitle('*Online PMC Activity Estimates Unavailable');
- end
- print('-opengl', '-dpng','-r350',[savepath_sub filesep char(subs(i)) '_daICtrial_offline-online_plot.png']);
- hold off
- pause; close;
- end
- end
- %%
- if group_level == 1
- % find grand mean & SEM of z-scored online target daIC activity
- group_avg_online_tar_daIC_epoch = mean(group_z_online_target_daIC_epochs,1);
- group_sem_online_tar_daIC_epoch = std(group_z_online_target_daIC_epochs, 0, 1) / sqrt(size(group_z_online_target_daIC_epochs,1));
- % mean & SEM of z-scored online nontarget PMC
- group_avg_online_nontar_PMC_epoch = mean(group_z_online_nontarget_PMC_epochs,1);
- group_sem_online_nontar_PMC_epoch = std(group_z_online_nontarget_PMC_epochs, 0, 1) / sqrt(size(group_z_online_nontarget_PMC_epochs,1));
- % mean & SEM z-scored offline daIC
- group_avg_offline_tar_daIC_epoch = mean(group_z_offline_target_daIC_epochs,1);
- group_sem_offline_tar_daIC_epoch = std(group_z_offline_target_daIC_epochs, 0, 1) / sqrt(size(group_z_offline_target_daIC_epochs, 1));
- % mean & SEM z-scoredoffline PMC
- group_avg_offline_nontar_PMC_epoch = mean(group_z_offline_nontarget_PMC_epochs,1);
- group_sem_offline_nontar_PMC_epoch = std(group_z_offline_nontarget_PMC_epochs, 0, 1) / sqrt(size(group_z_offline_nontarget_PMC_epochs,1));
- % Graph
- figure('Position', [500 500 1200 500])
- %figure('Position', [900 500 1500 500])
- a = shadedErrorBar([], group_avg_online_tar_daIC_epoch, group_sem_online_tar_daIC_epoch, 'lineprops', {'color', [1, 0, 0]}, 'patchSaturation', 0.08);
- hold on
- b = shadedErrorBar([], group_avg_online_nontar_PMC_epoch, group_sem_online_nontar_PMC_epoch, 'lineprops', {'color',[0, 0, 1]}, 'patchSaturation', 0.08);
- c = shadedErrorBar([], group_avg_offline_tar_daIC_epoch, group_sem_offline_tar_daIC_epoch, 'lineprops', {'color',[0.6, 0, 0]}, 'patchSaturation', 0.08);
- d = shadedErrorBar([], group_avg_offline_nontar_PMC_epoch, group_sem_offline_nontar_PMC_epoch, 'lineprops', {'color',[0, 0, 0.6]}, 'patchSaturation', 0.08);
- set(a.edge, 'LineWidth', 2, 'LineStyle', ':')
- a.mainLine.LineWidth = 4;
- set(b.edge, 'LineWidth', 2, 'LineStyle', ':')
- b.mainLine.LineWidth = 4;
- set(c.edge, 'LineWidth', 2, 'LineStyle', ':')
- c.mainLine.LineWidth = 4;
- set(d.edge, 'LineWidth', 2, 'LineStyle', ':')
- d.mainLine.LineWidth = 4;
- xline(epoch_range+1, '_', {'Trial Onset'},'LabelVerticalAlignment','bottom','LabelHorizontalAlignment','center', 'FontSize', 18, 'LineWidth', 2)
- xlabel('Time (seconds)', 'FontSize', 32)
- xticks([1:17])
- xticklabels({'-16','-14','-12','-10', '-8', '-6', '-4', '-2', '0', '2', '4', '6', '8', '10', '12','14','16'})
- ax=gca;
- ax.XAxis.FontSize = 28;
- % ylim([-1.5 2.0]);
- ylabel('BOLD Activity (z)', 'FontSize', 32)
- ax.YAxis.FontSize = 28;
- subj_nums = [];
- for i=1:length(subs)
- n = str2double(extractAfter(subs(i), "sub-"));
- subj_nums= [subj_nums; n];
- end
- legend({'Real-time daIC Activity (Target)',...
- ['Real-time PMC Activity (Non-target; n=' num2str(sum(~(subj_nums<20))) ')'],...
- 'Offline daIC Activity (Target)',...
- 'Offline PMC Activity (Non-target)'}, ...
- 'FontSize', 20, 'Location', 'best');
- title('Validation of daIC Trigger Events', 'FontSize', 30);
- print('-opengl', '-dpng','-r350',[savepath_group filesep 'group_n' num2str(length(subj_nums)) '_daICtrial_offline-online_plot.png']);
- hold off
- end
- %% Graph activity around when PMC trials were triggered
- if group_level == 1
- group_online_target_PMC_epochs = []; group_z_online_target_PMC_epochs = [];
- group_online_nontarget_daIC_epochs = []; group_z_online_nontarget_daIC_epochs = [];
- group_offline_target_PMC_epochs = []; group_z_offline_target_PMC_epochs = [];
- group_offline_nontarget_daIC_epochs = []; group_z_offline_nontarget_daIC_epochs = [];
- end
- for i=1:length(subs)
- runs = length(unique(master.run(master.subj == subs(i))));
- subj_num = str2double(extractAfter(subs(i), "sub-"));
- if subject_level == 1
- sub_online_target_PMC_epochs = []; sub_z_online_target_PMC_epochs = [];
- if subj_num > 20
- sub_online_nontarget_daIC_epochs = [];
- sub_z_online_nontarget_daIC_epochs = [];
- end
- sub_offline_target_PMC_epochs = []; sub_z_offline_target_PMC_epochs = [];
- sub_offline_nontarget_daIC_epochs = []; sub_z_offline_nontarget_daIC_epochs = [];
- end
- for j=1:runs
- target_ROI = unique(master.ROI(master.subj == subs(i) & master.run == j));
- % ensure we are only looking at runs that are triggering from the PMC
- if target_ROI == "PMC"
- nontarget_ROI = "daIC";
- %load in online target PMC data
- load([data_dir filesep 'subject_opennft_data' filesep char(subs(i)) filesep 'mainLoopData' filesep char(subs(i)) '_' num2str(j) '_mainLoopData.mat'], 'scalProcTimeSeries');
- online_target_PMC = scalProcTimeSeries(1,:);
- %load in online nontarget daIC data for subjects post-algorithm fix
- if subj_num > 20
- online_nontarget_daIC = scalProcTimeSeries(2,:);
- end
- %load in offline target PMC data
- subj_padded = sprintf('sub-%03d', subj_num);
- offline_target_PMC = (load([data_dir filesep 'analyses' filesep 'OfflineOnlineCompare' filesep 'OfflineTargetROITimeSeries' filesep subj_padded filesep 'run' num2str(j) filesep subj_padded '_run' num2str(j) '_preproc_meants.txt']))';
- %load in offline nontarget daIC data
- offline_nontarget_daIC = (load([data_dir filesep 'analyses' filesep 'OfflineOnlineCompare' filesep 'OfflineNontargetROITimeSeries' filesep subj_padded filesep 'run' num2str(j) filesep subj_padded '_run' num2str(j) '_preproc_meants.txt']))';
- trials = master.trial_num(master.subj == subs(i) & master.run == j);
- for k=1:length(trials)
- % ensure we're only looking at PMC-triggered trials
- if master.trigger_type(master.subj == subs(i) & master.run == j & master.trial_num == trials(k)) == "ROI"
- % epoch around ROI-triggered trials for this run - online
- % target PMC
- curr_trial = master.trial_onset_TR(master.subj == subs(i) & master.run == j & master.trial_num == trials(k));
- run_length = size(online_target_PMC,2);
- start_epoch = max(1, curr_trial - epoch_range);
- end_epoch = min(run_length, curr_trial + epoch_range);
- curr_online_tar_PMC_epoch = online_target_PMC(1, start_epoch:end_epoch);
- if group_level == 1
- group_online_target_PMC_epochs = [group_online_target_PMC_epochs; curr_online_tar_PMC_epoch];
- end
- if subject_level == 1
- sub_online_target_PMC_epochs = [sub_online_target_PMC_epochs; curr_online_tar_PMC_epoch];
- end
- curr_z_online_tar_PMC = (curr_online_tar_PMC_epoch - mean(curr_online_tar_PMC_epoch)) / std(curr_online_tar_PMC_epoch);
- if group_level == 1
- group_z_online_target_PMC_epochs = [group_z_online_target_PMC_epochs; curr_z_online_tar_PMC];
- end
- if subject_level == 1
- sub_z_online_target_PMC_epochs = [sub_z_online_target_PMC_epochs; curr_z_online_tar_PMC];
- end
- % epoch around ROI-triggered trials for this run - online
- % nontarget daIC
- if subj_num > 20
- run_length = size(online_nontarget_daIC,2);
- start_epoch = max(1, curr_trial - epoch_range);
- end_epoch = min(run_length, curr_trial + epoch_range);
- curr_online_nontar_daIC_epoch = online_nontarget_daIC(1, start_epoch:end_epoch);
- if group_level == 1
- group_online_nontarget_daIC_epochs = [group_online_nontarget_daIC_epochs; curr_online_nontar_daIC_epoch];
- end
- if subject_level == 1
- sub_online_nontarget_daIC_epochs = [sub_online_nontarget_daIC_epochs; curr_online_nontar_daIC_epoch];
- end
- curr_z_online_nontar_daIC = (curr_online_nontar_daIC_epoch - mean(curr_online_nontar_daIC_epoch)) / std(curr_online_nontar_daIC_epoch);
- if group_level == 1
- group_z_online_nontarget_daIC_epochs = [group_z_online_nontarget_daIC_epochs; curr_z_online_nontar_daIC];
- end
- if subject_level == 1
- sub_z_online_nontarget_daIC_epochs = [sub_z_online_nontarget_daIC_epochs; curr_z_online_nontar_daIC];
- end
- end
- % offline target PMC
- run_length = size(offline_target_PMC,2);
- start_epoch = max(1, curr_trial - epoch_range);
- end_epoch = min(run_length, curr_trial + epoch_range);
- curr_offline_tar_PMC_epoch = offline_target_PMC(1, start_epoch:end_epoch);
- if group_level == 1
- group_offline_target_PMC_epochs = [group_offline_target_PMC_epochs; curr_offline_tar_PMC_epoch];
- end
- if subject_level == 1
- sub_offline_target_PMC_epochs = [sub_offline_target_PMC_epochs; curr_offline_tar_PMC_epoch];
- end
- curr_z_offline_tar_PMC = (curr_offline_tar_PMC_epoch - mean(curr_offline_tar_PMC_epoch)) / std(curr_offline_tar_PMC_epoch);
- if group_level == 1
- group_z_offline_target_PMC_epochs = [group_z_offline_target_PMC_epochs; curr_z_offline_tar_PMC];
- end
- if subject_level == 1
- sub_z_offline_target_PMC_epochs = [sub_z_offline_target_PMC_epochs; curr_z_offline_tar_PMC];
- end
- % offline nontarget daIC
- run_length = size(offline_nontarget_daIC,2);
- start_epoch = max(1, curr_trial - epoch_range);
- end_epoch = min(run_length, curr_trial + epoch_range);
- curr_offline_nontar_daIC_epoch = offline_nontarget_daIC(1, start_epoch:end_epoch);
- if group_level == 1
- group_offline_nontarget_daIC_epochs = [group_offline_nontarget_daIC_epochs; curr_offline_nontar_daIC_epoch];
- end
- if subject_level == 1
- sub_offline_nontarget_daIC_epochs = [sub_offline_nontarget_daIC_epochs; curr_offline_nontar_daIC_epoch];
- end
- curr_z_offline_nontar_daIC = (curr_offline_nontar_daIC_epoch - mean(curr_offline_nontar_daIC_epoch)) / std(curr_offline_nontar_daIC_epoch);
- if group_level == 1
- group_z_offline_nontarget_daIC_epochs = [group_z_offline_nontarget_daIC_epochs; curr_z_offline_nontar_daIC];
- end
- if subject_level == 1
- sub_z_offline_nontarget_daIC_epochs = [sub_z_offline_nontarget_daIC_epochs; curr_z_offline_nontar_daIC];
- end
- end
- end
- elseif target_ROI == "daIC"
- nontarget_ROI = "PMC";
- end
- end
- if subject_level == 1
- % find grand mean & SEM of z-scored online target PMC activity
- sub_avg_online_tar_PMC_epoch = mean(sub_z_online_target_PMC_epochs,1);
- sub_sem_online_tar_PMC_epoch = std(sub_z_online_target_PMC_epochs, 0, 1) / sqrt(size(sub_z_online_target_PMC_epochs,1));
- % mean & SEM of z-scored online nontarget daIC
- if subj_num > 20
- sub_avg_online_nontar_daIC_epoch = mean(sub_z_online_nontarget_daIC_epochs,1);
- sub_sem_online_nontar_daIC_epoch = std(sub_z_online_nontarget_daIC_epochs, 0, 1) / sqrt(size(sub_z_online_nontarget_daIC_epochs,1));
- end
- % mean & SEM z-scored offline PMC
- sub_avg_offline_tar_PMC_epoch = mean(sub_z_offline_target_PMC_epochs,1);
- sub_sem_offline_tar_PMC_epoch = std(sub_z_offline_target_PMC_epochs, 0, 1) / sqrt(size(sub_z_offline_target_PMC_epochs, 1));
- % mean & SEM z-scoredoffline daIC
- sub_avg_offline_nontar_daIC_epoch = mean(sub_z_offline_nontarget_daIC_epochs,1);
- sub_sem_offline_nontar_daIC_epoch = std(sub_z_offline_nontarget_daIC_epochs, 0, 1) / sqrt(size(sub_z_offline_nontarget_daIC_epochs,1));
- % Graph
- figure('Position', [900 500 1500 500])
- a = shadedErrorBar([], sub_avg_online_tar_PMC_epoch, sub_sem_online_tar_PMC_epoch, 'lineprops', {'color', [0, 0, 1]}, 'patchSaturation', 0.08);
- hold on
- if subj_num > 20
- b = shadedErrorBar([], sub_avg_online_nontar_daIC_epoch, sub_sem_online_nontar_daIC_epoch, 'lineprops', {'color',[1, 0, 0]}, 'patchSaturation', 0.08);
- c = shadedErrorBar([], sub_avg_offline_tar_PMC_epoch, sub_sem_offline_tar_PMC_epoch, 'lineprops', {'color',[0, 0, 0.6]}, 'patchSaturation', 0.08);
- d = shadedErrorBar([], sub_avg_offline_nontar_daIC_epoch, sub_sem_offline_nontar_daIC_epoch, 'lineprops', {'color',[0.6, 0, 0]}, 'patchSaturation', 0.08);
- else
- b = shadedErrorBar([], sub_avg_offline_tar_PMC_epoch, sub_sem_offline_tar_PMC_epoch, 'lineprops', {'color',[0, 0, 0.6]}, 'patchSaturation', 0.08);
- c = shadedErrorBar([], sub_avg_offline_nontar_daIC_epoch, sub_sem_offline_nontar_daIC_epoch, 'lineprops', {'color',[0.6, 0, 0]}, 'patchSaturation', 0.08);
- end
- if subj_num > 20
- set(a.edge, 'LineWidth', 2, 'LineStyle', ':')
- a.mainLine.LineWidth = 4;
- set(b.edge, 'LineWidth', 2, 'LineStyle', ':')
- b.mainLine.LineWidth = 4;
- set(c.edge, 'LineWidth', 2, 'LineStyle', ':')
- c.mainLine.LineWidth = 4;
- set(d.edge, 'LineWidth', 2, 'LineStyle', ':')
- d.mainLine.LineWidth = 4;
- else
- set(a.edge, 'LineWidth', 2, 'LineStyle', ':')
- a.mainLine.LineWidth = 4;
- set(b.edge, 'LineWidth', 2, 'LineStyle', ':')
- b.mainLine.LineWidth = 4;
- set(c.edge, 'LineWidth', 2, 'LineStyle', ':')
- c.mainLine.LineWidth = 4;
- end
- xline(epoch_range+1, '_', {'Trial Onset'}, 'FontSize', 18, 'LineWidth', 2)
- xlabel('TR', 'FontSize', 20)
- xticks([1:17])
- xticklabels({'-8','-7','-6','-5', '-4', '-3', '-2', '-1', '0', '1', '2', '3', '4', '5', '6','7','8'})
- ax=gca;
- ax.XAxis.FontSize = 25;
- % ylim([-1.5 2.0]);
- ylabel('BOLD Activity (z)', 'FontSize', 20)
- ax.YAxis.FontSize = 20;
- if subj_num > 20
- legend({'Real-time PMC Activity (Target)', 'Real-time daIC Activity (Non-target)', 'Offline PMC Activity (Target)', 'Offline daIC Activity (Non-target)'}, 'FontSize', 18, 'Location', 'best');
- else
- legend({'Real-time PMC Activity (Target)', 'Offline PMC Activity (Target)', 'Offline daIC Activity (Non-target)'}, 'FontSize', 18, 'Location', 'best');
- end
- title({char(subs(i)),'Real-time vs Offline Estimates of Average Region Activity During PMC-triggered Trials'}, 'FontSize', 23);
- if subj_num <20
- subtitle('*Online daIC Activity Estimates Unavailable');
- end
- print('-opengl', '-dpng',[savepath_sub filesep char(subs(i)) '_PMCtrial_offline-online_plot.png']);
- hold off
- pause; close;
- end
- end
- %%
- if group_level == 1
- % find grand mean & SEM of z-scored online target PMC activity
- group_avg_online_tar_PMC_epoch = mean(group_z_online_target_PMC_epochs,1);
- group_sem_online_tar_PMC_epoch = std(group_z_online_target_PMC_epochs, 0, 1) / sqrt(size(group_z_online_target_PMC_epochs,1));
- % mean & SEM of z-scored online nontarget daIC
- group_avg_online_nontar_daIC_epoch = mean(group_z_online_nontarget_daIC_epochs,1);
- group_sem_online_nontar_daIC_epoch = std(group_z_online_nontarget_daIC_epochs, 0, 1) / sqrt(size(group_z_online_nontarget_daIC_epochs,1));
- % mean & SEM z-scored offline PMC
- group_avg_offline_tar_PMC_epoch = mean(group_z_offline_target_PMC_epochs,1);
- group_sem_offline_tar_PMC_epoch = std(group_z_offline_target_PMC_epochs, 0, 1) / sqrt(size(group_z_offline_target_PMC_epochs, 1));
- % mean & SEM z-scoredoffline daIC
- group_avg_offline_nontar_daIC_epoch = mean(group_z_offline_nontarget_daIC_epochs,1);
- group_sem_offline_nontar_daIC_epoch = std(group_z_offline_nontarget_daIC_epochs, 0, 1) / sqrt(size(group_z_offline_nontarget_daIC_epochs,1));
- % Graph
- figure('Position', [500 500 1200 500])
- %figure('Position', [900 500 1500 500])
- a = shadedErrorBar([], group_avg_online_tar_PMC_epoch, group_sem_online_tar_PMC_epoch, 'lineprops', {'color', [0, 0, 1]}, 'patchSaturation', 0.08);
- hold on
- b = shadedErrorBar([], group_avg_online_nontar_daIC_epoch, group_sem_online_nontar_daIC_epoch, 'lineprops', {'color',[1, 0, 0]}, 'patchSaturation', 0.08);
- c = shadedErrorBar([], group_avg_offline_tar_PMC_epoch, group_sem_offline_tar_PMC_epoch, 'lineprops', {'color',[0, 0, 0.6]}, 'patchSaturation', 0.08);
- d = shadedErrorBar([], group_avg_offline_nontar_daIC_epoch, group_sem_offline_nontar_daIC_epoch, 'lineprops', {'color',[0.6, 0, 0]}, 'patchSaturation', 0.08);
- set(a.edge, 'LineWidth', 2, 'LineStyle', ':')
- a.mainLine.LineWidth = 4;
- set(b.edge, 'LineWidth', 2, 'LineStyle', ':')
- b.mainLine.LineWidth = 4;
- set(c.edge, 'LineWidth', 2, 'LineStyle', ':')
- c.mainLine.LineWidth = 4;
- set(d.edge, 'LineWidth', 2, 'LineStyle', ':')
- d.mainLine.LineWidth = 4;
- xline(epoch_range+1, '_', {'Trial Onset'}, 'LabelVerticalAlignment', 'bottom', 'LabelHorizontalAlignment','center','FontSize', 18, 'LineWidth', 2)
- xlabel('Time (seconds)', 'FontSize', 32)
- xticks([1:17])
- xticklabels({'-16','-14','-12','-10', '-8', '-6', '-4', '-2', '0', '2', '4', '6', '8', '10', '12','14','16'})
- ax=gca;
- ax.XAxis.FontSize = 28;
- ylim([-1 1]);
- yticks(-1:0.5:1)
- ylabel('BOLD Activity (z)', 'FontSize', 32)
- ax.YAxis.FontSize = 28;
- subj_nums = [];
- for i=1:length(subs)
- n = str2double(extractAfter(subs(i), "sub-"));
- subj_nums= [subj_nums; n];
- end
- legend({'Real-time PMC Activity (Target)',...
- ['Real-time daIC Activity (Non-target; n=' num2str(sum(~(subj_nums<20))) ')'],...
- 'Offline PMC Activity (Target)',...
- 'Offline daIC Activity (Non-target)'}, ...
- 'FontSize', 20, 'Location', 'best');
- title('Validation of PMC Trigger Events', 'FontSize', 30);
- print('-opengl', '-dpng','-r350',[savepath_group filesep 'group_n' num2str(length(subj_nums)) '_PMCtrial_offline-online_plot.png']);
- hold off
- end
offlineonlineplots.m, no license · at the source
Overview
Abstract
Much of a typical individual’s mental life is characterized by spontaneous thoughts that occur independently of external stimuli. In prior studies, ongoing mental experiences and their neural correlates have been captured using thought probes presented at random intervals during functional magnetic resonance imaging (fMRI). However, this approach results in temporally imprecise estimates of brain activity relative to the arising of mental experience. In this preregistered, proof-of-concept study, we aimed to improve temporal precision using a novel method termed real-time fMRI-triggered experience sampling (rt-fMRI-ES). We analyzed blood-oxygenation level-dependent signals in real time during a wakeful resting state (n = 60) to trigger thought probes from spontaneous activations within two regions: the dorsal anterior insular cortex (daIC; a key region within salience network) and posteromedial cortex (PMC; a key region within default mode network). We tested two preregistered hypotheses: (H1) Ratings of arousal time-locked to daIC-activation trials are higher than ratings time-locked to non-daIC-activation trials; (H2) Ratings of external attention time locked to PMC-activation trials are lower than ratings time-locked to non-PMC-activation trials. After applying preregistered exclusion criteria, 42 participants (1243 trials) and 49 participants (1429 trials) were included in H1 and H2 analyses, respectively. We did not find evidence in support of H1, but we did find evidence in support of H2, as external-attention ratings were significantly lower for trials triggered by PMC activation than other trial types. Taken together, we successfully developed and validated the rt-fMRI-ES method, offering a novel technique to efficiently capture spontaneous thoughts based on ongoing neural activity. Preregistered Stage 1 Recommendation: https://
Reproduced under the paper's license (CC BY), from the paper cited above.
Repositories
Its files are read in the Code ↔ Paper reader above, with 5 matches between paragraphs and lines of code.
DynamicBrainMind/rtfMRI_prep
0cbac2e31b941c916ef587cabd015d5a101ccfa0, 18 June 2025Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
5 files
- gradCPT_mk_Mt_EV.m, MATLAB, 81 lines, 1 match
- tests/
testRTexp.py , Python, 70 lines - tests/
testUDPReceiver.py , Python, 45 lines - tests/
testUDPSender.py , Python, 44 lines - README.md, Text, 27 lines
OSF as2q9
Availability: 1 check, the latest on 27 September 2026: the link answers (HTTP 200)
- 27 September 2026: the link answers (HTTP 200)
5 files
- Code/
Figure 2/ , MATLAB, 63 lines, 1 matchexclusionbarplot.m - Code/
Figure 2/ , R, 67 linespiechart_trialtype.R - Code/
Figure 2/ , MATLAB, 111 linesplot_trialtypebreakdown. m - Code/
Figure 3/ , MATLAB, 758 lines, 2 matchesofflineonlineplots.m - Code/
Figure 4 + Pre-registered Hypothesis Testing/ , R, 324 lines, 1 matchrtfMRI_HypothesisTesting .R
The paper's code and data availability statement is in the Data section.
Tracing map
Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.
What the map holds:
- 2 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
- 9 scripts, each with its path and the digest of its content;
- 5 matches between paragraphs of the paper and lines of the code (method lexical-v1);
- neither the text of the paper nor the code itself.
Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.
Data
No dataset and no data link were found in the paper.
Data and Code Availability
In accordance with requirements set by the project sponsor, all study raw data will be available through the online National Institutes of Mental Health Data Archive (NDA), DOI: https://
Reproduced under the paper's license (CC BY), from the paper cited above.
Versions
The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.
Version 2, 28 September 2026
- Funding: added National Institute of Mental Health: R21 MH129630; National Science Foundation Graduate Research Fellowship Program
Version 1, 27 September 2026: the first record
Recorded: type, language, journal, volume, pages, dates, 3 authors, 6 keywords, 91 references.
Cite
This paper
Bounyarith, T., Braun, D., & Kucyi, A. (2026). Real-time fMRI-triggered experience sampling: A proof-of-concept study. Imaging neuroscience (Cambridge, Mass.), 4, IMAG.a.1268. https://
BibTeX
@article{bounyarith2026r
author = {Bounyarith, Tiara and Braun, David and Kucyi, Aaron},
title = {{Real-time fMRI-triggered experience sampling: A proof-of-concept study}},
journal = {Imaging neuroscience (Cambridge, Mass.)},
year = {2026},
month = jun,
volume = {4},
pages = {IMAG.a.1268},
publisher = {MIT Press},
issn = {2837-6056},
doi = {10.1162/
url = {https://
pmid = {42272744},
pmcid = {PMC13248897}
}
RIS
TY - JOUR
AU - Bounyarith, Tiara
AU - Braun, David
AU - Kucyi, Aaron
TI - Real-time fMRI-triggered experience sampling: A proof-of-concept study
T2 - Imaging neuroscience (Cambridge, Mass.)
J2 - Imaging Neurosci (Camb)
PY - 2026
DA - 2026/
VL - 4
SP - IMAG.a.1268
SN - 2837-6056
PB - MIT Press
DO - 10.1162/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1162/
"type": "article-journal",
"title": "Real-time fMRI-triggered experience sampling: A proof-of-concept study",
"container-title": "Imaging neuroscience (Cambridge, Mass.)",
"author": [
{
"family": "Bounyarith",
"given": "Tiara"
},
{
"family": "Braun",
"given": "David"
},
{
"family": "Kucyi",
"given": "Aaron"
}
],
"container-title-short":
"volume": "4",
"page": "IMAG.a.1268",
"DOI": "10.1162/
"PMID": "42272744",
"PMCID": "PMC13248897",
"ISSN": "2837-6056",
"publisher": "MIT Press",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
6,
8
]
]
}
}
The tracing map gets a citation of its own once an author has validated it and it has a DOI.
Similar papers
The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.
- [1] doi:10.1038/s41467-026-74331-2 [code]
- Trial-by-trial fMRI-neurofeedback dissociates fusiform and occipital contributions to face detection and recognition.Journal: Nature communicationsIn common: shadedErrorBar, emmeans, lmerTest, 6 other tools, fMRI, 3 references
- [2] doi:10.1038/s41598-026-49900-6 [code]
- Global neural oscillations underlie performance variability and attentional state fluctuations in humans.Journal: Scientific reportsIn common: lme4, Statistics and Machine Learning Toolbox, 9 references
- [3] doi:10.1002/jcv2.70135 [code]
- Alterations in resting-state functional connectivity relate to psychopathology trajectories during emerging adolescence.Journal: JCPP advancesIn common: emmeans, lmerTest, lme4, 3 other tools, fMRI, 3 references
- [4] doi:10.1038/s41467-026-74953-6 [code]
- In-scanner thoughts contribute to resting-state functional connectivity.Journal: Nature communicationsIn common: NumPy, fMRI, 7 references
- [5] doi:10.1073/pnas.2603114123 [code]
- The human hippocampus can pattern separate memories by meaning.Journal: Proceedings of the National Academy of Sciences of the United States of AmericaIn common: emmeans, lmerTest, lme4, 4 other tools, 3 references
- [6] doi:10.1038/s41467-026-73865-9 [code]
- Histamine shapes the neurocomputational dynamics of human learning.Journal: Nature communicationsIn common: emmeans, lmerTest, lme4, 3 other tools, 4 references
- [7] doi:10.1371/journal.pone.0353990 [code]
- Positive mood enhances accessibility of unrelated concepts in the first language but not in the foreign language.Journal: PloS oneIn common: emmeans, lmerTest, lme4, 3 other tools, 3 references
- [8] doi:10.1016/j.neuroimage.2026.122115 [code]
- Midfrontal theta power relates to response speeding following frustrative nonreward.Journal: NeuroImageIn common: emmeans, lmerTest, lme4, 4 other tools, 2 references
- [9] doi:10.1162/imag.a.105 [code]
- Right posterior theta reflects human parahippocampal phase resetting by salient cues during goal-directed navigationJournal: n/aIn common: lmerTest, lme4, Statistics and Machine Learning Toolbox, 3 other tools, fMRI, 3 references
- [10] doi:10.3758/s13415-026-01442-0 [code]
- Empathy for pain persists across live two-way video interactions and viewing of prerecorded videos.Journal: Cognitive, affective & behavioral neuroscienceIn common: lmerTest, lme4, ggplot2, 1 other tool, 5 references
Contribute
The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.
Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 2 repositories of the authors' code, each at its verified commit and with its license, 9 scripts, and 5 matches between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:fc35d194dfa59b1d…
Add the badge to its README
The badge links the code to this page. Copy one of these into the README of the paper's code: only you decide where it goes, and nothing is changed for you.
Markdown
[, paste the snippet at the top, then “Commit changes…” and, to review it first, “Create a new branch and start a pull request”. You open the pull request; OSCR asks for no permission.
Request its removal
To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).
Discussion, reproductions, activity
Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.
Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.
Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.
