Gamer in the scanner: Event-related analysis of fMRI activity during retro videogame play guided by automated annotations of game content.
The 23 matches
- [1] § Methods › Annotation of task events › Sensory and motor features ↔ shinobi_fmri/glm/utils.py, lines 565–621 · score 0.87 · canonical HRF, Optical flow, Audio envelope, task regressors, Button press, downsampled
- [2] § Methods › Statistical analyses › First-level (session) general linear model ↔ shinobi_fmri/glm/compute_session_level.py, lines 1–59 · score 0.85 · minimum cluster extent, noise model, fMRIPrep, AR, design matrix, FWHM
- [3] § Methods › Statistical analyses › Brain activation maps derived from HCP test-retest dataset ↔ shinobi_fmri/visualization/hcp_tasks.py, lines 35–44 · score 0.82 · left_foot, left_hand, right_foot, right_hand, cues, tongue
- [4] § Methods › Statistical analyses › Brain activation maps derived from HCP test-retest dataset ↔ notebooks/NB5_beta_correlations.ipynb, lines 117–128 · score 0.81 · left_foot, left_hand, right_foot, right_hand, cues, tongue
- [5] § Methods › Statistical analyses › First-level (session) general linear model ↔ shinobi_fmri/glm/utils.py, lines 721–793 · score 0.78 · global signal, fMRIPrep, design matrix, convolved, CSF, SPM
- [6] § Methods › Neuroimaging acquisitions › The shinobi dataset › At-home training ↔ shinobi_fmri/visualization/viz_training_comparison.py, lines 1–72 · score 0.73 · shinobi_training, home training, scanner sessions, weeks, metrics, behavioral
- [7] § Methods › Annotation of task events › Memory (RAM) mining ↔ notebooks/NB2_replay_visualizations.ipynb, lines 172–214 · score 0.66 · Replay bk2, gym retro, game variables
- [8] § Results › Within- and between-participant variability of brain maps compared to classical functional localizer tasks ↔ shinobi_fmri/visualization/viz_beta_correlations.py, lines 349–414 · score 0.64 · Optical flow, Audio envelope, Button press, HealthLoss, SD, Inter
- [9] § Results › Within- and between-participant variability of brain maps compared to classical functional localizer tasks ↔ shinobi_fmri/visualization/viz_beta_correlations.py, lines 349–414 · score 0.64 · Optical flow, Audio envelope, Button press, HealthLoss, SD, inter
- [10] § Methods › Statistical analyses › Second-level (participant) general linear model ↔ shinobi_fmri/glm/compute_session_level.py, lines 1–59 · score 0.62 · minimum cluster extent, nilearn, smoothing, uncorrected, FDR, voxel
- [11] § Results › GLM regressors had low to moderate correlations for most game events ↔ shinobi_fmri/visualization/viz_regressor_correlations.py, lines 465–517 · score 0.60 · optical flow, Audio envelope, button press, HealthLoss, luminance, regressors
- [12] § Results › Within- and between-participant variability of brain maps compared to classical functional localizer tasks ↔ shinobi_fmri/visualization/viz_regressor_correlations.py, lines 465–517 · score 0.60 · Optical flow, Audio envelope, Button press, HealthLoss, Luminance, regressors
- [13] § Methods › Statistical analyses › Second-level (participant) general linear model ↔ shinobi_fmri/glm/compute_subject_level.py, lines 1–66 · score 0.60 · minimum cluster extent, intercept, smoothing, uncorrected, FDR, voxel
- [14] § Methods › Annotation of task events › Data structure for events ↔ shinobi_fmri/visualization/viz_regressor_correlations.py, lines 93–235 · score 0.60 · annotated_events.tsv, button press, HealthLoss, onset, desc, Rows
- [15] § Results › Game events activation maps are discriminable ↔ tasks.py, lines 23–40 · score 0.60 · optical flow, audio envelope, button press, HealthLoss, luminance, errors
- [16] § Methods › Neuroimaging acquisitions › MRI preprocessing ↔ shinobi_fmri/glm/utils.py, lines 721–793 · score 0.56 · wm_csf, high_pass, motion, preprocessing, MRI
- [17] § Methods › Neuroimaging acquisitions › MRI preprocessing ↔ shinobi_fmri/visualization/viz_regressor_correlations.py, lines 93–235 · score 0.55 · wm_csf, high_pass, motion, preprocessing
- [18] § Methods › Neuroimaging acquisitions › The shinobi dataset › At-home training ↔ tasks.py, lines 1240–1275 · score 0.55 · home training, scanner sessions, metrics, behavioral, shinobi
- [19] § Methods › Annotation of task events › Data structure for events ↔ shinobi_fmri/descriptive/dataset_summary.py, lines 42–186 · score 0.55 · annotated_events.tsv, HealthLoss, fMRI, bk2, desc, Rows
- [20] § Methods › Behavioral acquisitions › Videogame interface ↔ shinobi_fmri/annotations/generate_train_json.py, lines 125–135 · score 0.55 · Ninja Master, Shinobi III, replays, variables, player, game
- [21] § Methods › Statistical analyses › MVPA classification ↔ shinobi_fmri/mvpa/compute_mvpa.py, lines 131–229 · score 0.54 · dummy classifier, confusion matrix, MVPA, Model
- [22] § Methods › Statistical analyses › MVPA classification ↔ shinobi_fmri/mvpa/compute_mvpa.py, lines 398–454 · score 0.52 · dummy classifier, confusion matrix, MVPA
- [23] § Methods › Statistical analyses › Beta maps correlations ↔ notebooks/NB5_beta_correlations.ipynb, lines 11–87 · score 0.52 · inter annotation, beta maps, ICC, correlations
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
Python · 854 lines · 32 KB · MIT · 4 matches
- """
- Generate design matrices and correlation visualization for GLM regressors.
- This script computes and visualizes design matrices and correlations between
- key regressors in the GLM. It produces:
- 1. Design matrices (one per run) showing all regressors
- 2. 2x2 correlation grid showing subject-averaged correlations
- The 2x2 grid focuses on Shinobi task conditions and low-level confounds
- (psychophysics: luminance, optical_flow, audio_envelope; and button press:
- button_presses_count) to identify multicollinearity between task events and
- low-level visual/audio features and motor activity.
- """
- import os
- import os.path as op
- import argparse
- import pickle
- import logging
- import re
- import warnings
- from typing import Any, Dict, List, Optional
- import numpy as np
- import pandas as pd
- import matplotlib.pyplot as plt
- import seaborn as sns
- from tqdm import tqdm
- from nilearn.glm.first_level import make_first_level_design_matrix
- from nilearn.signal import clean
- import nilearn.interfaces.fmriprep
- import shinobi_fmri.config as config
- from shinobi_fmri.utils.logger import AnalysisLogger
- from shinobi_fmri.glm.utils import add_psychophysics_confounds, add_button_press_confounds
- from shinobi_fmri.visualization.hcp_tasks import SHINOBI_COLOR
- # Suppress specific warnings
- warnings.filterwarnings('ignore', category=UserWarning, module='nilearn')
- warnings.filterwarnings('ignore', category=UserWarning, module='sklearn')
- warnings.filterwarnings('ignore', category=DeprecationWarning)
- def setup_argparse():
- """Set up command-line argument parser."""
- parser = argparse.ArgumentParser(
- description="Generate correlation matrices for design matrix regressors"
- )
- parser.add_argument(
- "-s", "--subject",
- default=None,
- type=str,
- help="Specific subject to process (default: all subjects)",
- )
- parser.add_argument(
- "--data-path",
- default=config.path_to_data,
- type=str,
- help=f"Path to data directory (default: {config.path_to_data})",
- )
- parser.add_argument(
- "--figures-path",
- default=config.figures_path,
- type=str,
- help=f"Path to figures output directory (default: {config.figures_path})",
- )
- parser.add_argument(
- "--skip-generation",
- action="store_true",
- help="Skip design matrix generation, only plot from existing pickle file",
- )
- parser.add_argument(
- "--exclude-low-level",
- action="store_true",
- default=False,
- help="Exclude low-level confounds (psychophysics and button presses) from design matrix (default: False, confounds are included)",
- )
- parser.add_argument(
- "-v", "--verbose",
- action="count",
- default=0,
- help="Increase verbosity level (e.g. -v for INFO, -vv for DEBUG)",
- )
- parser.add_argument(
- "--log-dir",
- default=None,
- help="Directory for log files",
- )
- return parser
- def process_single_run(sub, ses, run, path_to_data, figures_path, exclude_low_level=False):
- """Helper function to process a single run."""
- t_r = 1.49
- hrf_model = 'spm'
- try:
- # Zero-pad run number for events filename (run-01 format)
- run_padded = run.zfill(2)
- # Build file paths
- # Build file paths
- events_base_path = op.join(path_to_data, "shinobi", sub, ses, "func")
- annotated_events = op.join(
- events_base_path,
- f"{sub}_{ses}_task-shinobi_run-{run_padded}_desc-annotated_events.tsv"
- )
- standard_events = op.join(
- events_base_path,
- f"{sub}_{ses}_task-shinobi_run-{run_padded}_events.tsv"
- )
- if op.exists(annotated_events):
- events_fname = annotated_events
- elif op.exists(standard_events):
- events_fname = standard_events
- else:
- events_fname = annotated_events # Default for error reporting
- fmri_fname = op.join(
- path_to_data, "shinobi.fmriprep",
- sub, ses, "func",
- f"{sub}_{ses}_task-shinobi_run-{run}_space-MNI152NLin2009cAsym_desc-preproc_bold.nii.gz",
- )
- # Check if events file exists
- if not op.exists(events_fname):
- return {
- 'success': False,
- 'subject': sub,
- 'session': ses,
- 'run': run,
- 'error': f"Events file not found: {events_fname}"
- }
- # Load events
- run_events = pd.read_csv(events_fname, sep='\t', low_memory=False)
- # Load confounds using nilearn interface (returns tuple)
- try:
- confounds = nilearn.interfaces.fmriprep.load_confounds(
- fmri_fname,
- strategy=("motion", "high_pass", "wm_csf"),
- motion="full",
- wm_csf="basic",
- global_signal="full",
- )
- except Exception as e:
- return {
- 'success': False,
- 'subject': sub,
- 'session': ses,
- 'run': run,
- 'error': f"Could not load confounds (broken link or missing file): {str(e)}"
- }
- # Add low-level confounds unless excluded
- if not exclude_low_level:
- confounds = add_psychophysics_confounds(confounds, run_events, path_to_data, t_r=t_r)
- confounds = add_button_press_confounds(confounds, run_events, t_r=t_r)
- # Extract DataFrame from tuple
- confounds = confounds[0]
- # Generate design matrix
- n_slices = confounds.shape[0]
- frame_times = np.arange(n_slices) * t_r
- # Filter events to keep only conditions from config (excludes gym-retro_game, etc.)
- events_cols = ['onset', 'duration', 'trial_type']
- if 'modulation' in run_events.columns:
- events_cols.append('modulation')
- events_df_clean = run_events[events_cols].copy()
- # Keep only conditions defined in config.CONDITIONS
- events_df_clean = events_df_clean[events_df_clean['trial_type'].isin(config.CONDITIONS)]
- # Drop rows with NaN onset (can't model events without onset time)
- events_df_clean = events_df_clean.dropna(subset=['onset'])
- # Suppress nilearn warning about zero-duration events (Kill, HealthLoss are impulse events)
- with warnings.catch_warnings():
- warnings.filterwarnings('ignore', message='.*null duration.*')
- design_matrix_raw = make_first_level_design_matrix(
- frame_times,
- events=events_df_clean,
- drift_model=None,
- hrf_model=hrf_model,
- add_regs=confounds,
- add_reg_names=None
- )
- # Clean design matrix
- regressors_clean = clean(
- design_matrix_raw.to_numpy(),
- detrend=True,
- standardize='zscore_sample',
- high_pass=None,
- t_r=t_r,
- ensure_finite=True,
- confounds=None,
- )
- design_matrix_clean = pd.DataFrame(
- regressors_clean,
- columns=design_matrix_raw.columns.to_list()
- )
- # Save design matrix plot
- from nilearn import plotting as nlplot
- design_matrix_clean_fname = op.join(
- figures_path, "design_matrices",
- f"design_matrix_clean_{sub}_{ses}_run-{run_padded}.png",
- )
- os.makedirs(op.dirname(design_matrix_clean_fname), exist_ok=True)
- nlplot.plot_design_matrix(
- design_matrix_clean,
- output_file=design_matrix_clean_fname
- )
- return {
- 'success': True,
- 'regressors': design_matrix_clean,
- 'subject': sub,
- 'session': ses,
- 'run': run
- }
- except Exception as e:
- return {
- 'success': False,
- 'subject': sub,
- 'session': ses,
- 'run': run,
- 'error': str(e)
- }
- def build_design_matrices(subjects, path_to_data, figures_path, exclude_low_level=False, logger=None):
- """
- Build design matrices for all runs across specified subjects.
- Args:
- subjects: List of subject IDs to process
- path_to_data: Path to data directory
- figures_path: Path to figures directory
- exclude_low_level: Exclude low-level confounds (psychophysics and button presses)
- logger: AnalysisLogger instance
- Returns:
- dict: Dictionary containing design matrices, subjects, sessions, and runs
- """
- regressors_dict = {
- 'regressors': [],
- 'subject': [],
- 'session': [],
- 'run': []
- }
- # Collect all tasks to run
- tasks = []
- # Pre-scan for tasks
- for sub in subjects:
- fmriprep_sub_path = op.join(path_to_data, "shinobi.fmriprep", sub)
- if not op.exists(fmriprep_sub_path):
- if logger:
- logger.warning(f"Subject directory not found: {fmriprep_sub_path}")
- continue
- sessions = [d for d in os.listdir(fmriprep_sub_path)
- if d.startswith('ses-') and op.isdir(op.join(fmriprep_sub_path, d))]
- for ses in sorted(sessions):
- ses_fpath = op.join(path_to_data, "shinobi.fmriprep", sub, ses, "func")
- if not op.exists(ses_fpath):
- if logger:
- logger.warning(f"Func directory not found: {ses_fpath}")
- continue
- ses_files = os.listdir(ses_fpath)
- run_files = [
- x for x in ses_files
- if "space-MNI152NLin2009cAsym_desc-preproc_bold.nii.gz" in x
- ]
- # Extract run numbers
- run_list = []
- for fname in run_files:
- match = re.search(r'run-(\d+)', fname)
- if match:
- run_list.append(match.group(1))
- for run in sorted(run_list):
- tasks.append((sub, ses, run))
- # Process sequentially
- if logger:
- logger.info(f"Processing {len(tasks)} runs...")
- for sub, ses, run in tqdm(tasks, desc="Building design matrices"):
- res = process_single_run(
- sub, ses, run, path_to_data, figures_path, exclude_low_level
- )
- if res['success']:
- regressors_dict['regressors'].append(res['regressors'])
- regressors_dict['subject'].append(res['subject'])
- regressors_dict['session'].append(res['session'])
- regressors_dict['run'].append(res['run'])
- if logger:
- logger.summary.add_computed(f"Design matrix: {res['subject']} {res['session']} run {res['run']}")
- else:
- if logger:
- logger.error(f"Error processing {res['subject']} {res['session']} run {res['run']}: {res['error']}")
- logger.summary.add_error(f"{res['subject']} {res['session']} run {res['run']}", res['error'])
- else:
- print(f"Error processing {res['subject']} {res['session']} run {res['run']}: {res['error']}")
- return regressors_dict
- def plot_run_correlations(regressors_dict, figures_path, logger=None):
- """
- Plot correlation matrices for each run.
- Args:
- regressors_dict: Dictionary containing design matrices
- figures_path: Path to save figures
- logger: AnalysisLogger instance
- """
- output_dir = op.join(figures_path, 'design_matrices')
- os.makedirs(output_dir, exist_ok=True)
- regressors_dict['corr_mat'] = []
- pbar = tqdm(enumerate(regressors_dict['regressors']),
- total=len(regressors_dict['regressors']),
- desc="Plotting run correlations")
- for run_idx, run in pbar:
- sub = regressors_dict['subject'][run_idx]
- ses = regressors_dict['session'][run_idx]
- run_num = regressors_dict['run'][run_idx]
- if logger:
- logger.info(f"Plotting correlations for {sub} {ses} run {run_num}")
- # Compute correlation matrix (excluding constant)
- corr = regressors_dict['regressors'][run_idx].corr()
- if 'constant' in corr.index:
- corr = corr.drop(index='constant').drop(columns='constant')
- regressors_dict['corr_mat'].append(corr)
- mask = np.triu(np.ones_like(corr, dtype=bool))
- # Heatmap
- f, ax = plt.subplots(figsize=(30, 25))
- sns.heatmap(
- corr, mask=mask, center=0,
- square=True, linewidths=.5, annot=False,
- cbar_kws={"shrink": .5}
- ).set_title(f'{sub} {ses} run {run_num}')
- fig_fname = op.join(
- output_dir,
- f'regressor_correlations_{sub}_{ses}_run-{run_num}.png'
- )
- plt.savefig(fig_fname, dpi=100, bbox_inches='tight')
- plt.close()
- # Cluster map (only if no NaN/inf values)
- if np.all(np.isfinite(corr.values)):
- try:
- f = sns.clustermap(
- corr, mask=mask, figsize=(30, 25),
- cbar_kws={"shrink": .5}
- )
- fig_fname = op.join(
- output_dir,
- f'regressor_correlations_{sub}_{ses}_run-{run_num}_cluster.png'
- )
- f.savefig(fig_fname, dpi=100, bbox_inches='tight')
- plt.close()
- except ValueError as e:
- if logger:
- logger.warning(f"Could not create clustermap for {sub} {ses} run {run_num}: {str(e)}")
- if logger:
- logger.summary.add_computed(f"Run correlations: {sub} {ses} run {run_num}")
- pbar.set_postfix({"Subject": sub, "Session": ses, "Run": run_num})
- def plot_subject_averaged_correlations(regressors_dict, subjects, figures_path, exclude_low_level=False, logger=None):
- """
- Plot subject-averaged correlation matrices.
- Args:
- regressors_dict: Dictionary containing correlation matrices
- subjects: List of subjects to process
- figures_path: Path to save figures
- exclude_low_level: Exclude low-level confounds from design matrix
- logger: AnalysisLogger instance
- """
- output_dir = op.join(figures_path, 'design_matrices')
- os.makedirs(output_dir, exist_ok=True)
- # Add suffix to filename if low-level confounds are included
- suffix = "" if exclude_low_level else "_low-level"
- for sub in tqdm(subjects, desc="Plotting subject-averaged correlations"):
- if logger:
- logger.info(f"Plotting averaged correlations for {sub}")
- # Collect all correlation matrices for this subject
- subj_corrs = []
- for idx, corr_mat in enumerate(regressors_dict['corr_mat']):
- if regressors_dict['subject'][idx] == sub:
- subj_corrs.append(corr_mat)
- if not subj_corrs:
- if logger:
- logger.warning(f"No correlation matrices found for {sub}")
- continue
- # Average across runs
- averaged_corr_mat = pd.concat(subj_corrs, axis=0).groupby(level=0).mean()
- mask = np.triu(np.ones_like(averaged_corr_mat, dtype=bool))
- # Heatmap with annotations
- f, ax = plt.subplots(figsize=(30, 25))
- title = f'{sub} - Averaged across runs'
- if not exclude_low_level:
- title += ' (with low-level confounds)'
- sns.heatmap(
- averaged_corr_mat, mask=mask, center=0,
- square=True, linewidths=.5, annot=True,
- cbar_kws={"shrink": .5}, fmt='.2f'
- ).set_title(title)
- fig_fname = op.join(output_dir, f'regressor_correlations_{sub}{suffix}.png')
- plt.savefig(fig_fname, dpi=100, bbox_inches='tight')
- plt.close()
- # Cluster map (only if no NaN/inf values)
- if np.all(np.isfinite(averaged_corr_mat.values)):
- try:
- f = sns.clustermap(
- averaged_corr_mat, mask=mask, figsize=(30, 25),
- center=0, square=True, linewidths=.5, annot=True,
- cbar_kws={"shrink": .5}, fmt='.2f'
- )
- fig_fname = op.join(output_dir, f'regressor_correlations_{sub}_cluster{suffix}.png')
- f.savefig(fig_fname, dpi=100, bbox_inches='tight')
- plt.close()
- except ValueError as e:
- if logger:
- logger.warning(f"Could not create clustermap for {sub}: {str(e)}")
- else:
- if logger:
- logger.warning(f"Skipping clustermap for {sub} due to non-finite correlation values")
- if logger:
- logger.summary.add_computed(f"Subject-averaged correlations: {sub}")
- def plot_2x2_subject_correlations(regressors_dict, subjects, figures_path, exclude_low_level=False, logger=None):
- """
- Plot a 2x2 grid of subject-averaged correlation matrices.
- Creates a compact visualization showing the lower triangle of each subject's
- averaged correlation matrix in a 2x2 layout with a shared red-white-blue colorbar.
- Only includes 8 Shinobi task conditions (DOWN, HIT, HealthGain, HealthLoss,
- JUMP, Kill, LEFT, RIGHT), 3 psychophysics confounds (luminance,
- optical_flow, audio_envelope), and button press confounds (button_presses_count).
- Correlation values are displayed in each cell with 2 decimal places.
- Args:
- regressors_dict: Dictionary containing correlation matrices
- subjects: List of subjects to process (should be 4 subjects for 2x2 grid)
- figures_path: Path to save figures
- exclude_low_level: Exclude low-level confounds from design matrix
- logger: AnalysisLogger instance
- """
- output_dir = op.join(figures_path, 'design_matrices')
- os.makedirs(output_dir, exist_ok=True)
- # Add suffix to filename if low-level confounds are included
- suffix = "" if exclude_low_level else "_low-level"
- if logger:
- logger.info(f"Plotting 2x2 grid for {len(subjects)} subjects")
- # Define which regressors to include and their order
- # Match order from descriptive_annotations: RIGHT, LEFT, DOWN, HIT, JUMP, Kill, HealthLoss
- # Then add low-level features
- shinobi_conditions = ['RIGHT', 'LEFT', 'DOWN', 'HIT', 'JUMP', 'Kill', 'HealthLoss']
- # Psychophysics confounds (internal names)
- psychophysics_confounds = ['luminance', 'optical_flow', 'audio_envelope']
- # Button press confounds
- button_confounds = ['button_presses_count']
- # Combined list in desired order
- regressors_to_include = shinobi_conditions + psychophysics_confounds + button_confounds
- # Display names for cleaner labels (all should be displayed)
- display_names = {
- 'RIGHT': 'RIGHT',
- 'LEFT': 'LEFT',
- 'DOWN': 'DOWN',
- 'HIT': 'HIT',
- 'JUMP': 'JUMP',
- 'Kill': 'Kill',
- 'HealthLoss': 'HealthLoss',
- 'luminance': 'Luminance',
- 'optical_flow': 'Optical flow',
- 'audio_envelope': 'Audio envelope',
- 'button_presses_count': 'Button press'
- }
- # Prepare averaged correlation matrices for each subject
- subject_corr_mats = {}
- all_values = [] # To compute global vmin/vmax
- for sub in subjects:
- # Collect all correlation matrices for this subject
- subj_corrs = []
- for idx, corr_mat in enumerate(regressors_dict['corr_mat']):
- if regressors_dict['subject'][idx] == sub:
- subj_corrs.append(corr_mat)
- if not subj_corrs:
- if logger:
- logger.warning(f"No correlation matrices found for {sub}")
- continue
- # Average across runs
- averaged_corr_mat = pd.concat(subj_corrs, axis=0).groupby(level=0).mean()
- # Filter to keep only specified regressors (in the specified order)
- available_regressors = [r for r in regressors_to_include if r in averaged_corr_mat.index]
- if len(available_regressors) == 0:
- if logger:
- logger.warning(f"No target regressors found for {sub}")
- logger.warning(f"Available columns: {list(averaged_corr_mat.columns)}")
- continue
- # Reorder to match desired order
- averaged_corr_mat = averaged_corr_mat.loc[available_regressors, available_regressors]
- subject_corr_mats[sub] = averaged_corr_mat
- if logger:
- logger.info(f"{sub}: Found {len(available_regressors)} regressors: {available_regressors}")
- # Collect values from lower triangle for global color scale
- mask_lower = np.tril(np.ones_like(averaged_corr_mat, dtype=bool), k=-1)
- all_values.extend(averaged_corr_mat.values[mask_lower])
- if not subject_corr_mats:
- if logger:
- logger.error("No correlation matrices to plot")
- return
- # Compute and save pairwise correlation statistics table (all regressors including low-level)
- stats_df = compute_pairwise_correlation_stats(subject_corr_mats, regressors_to_include)
- save_correlation_stats_table(stats_df, config.TABLE_PATH, suffix, logger)
- # Print key pairs to console for quick manuscript reference
- key_pairs = [
- ('HIT', 'Kill'), ('DOWN', 'HIT'), ('DOWN', 'Kill'),
- ('JUMP', 'HIT'), ('RIGHT', 'LEFT'),
- ]
- print("\nFig 5 – Pairwise Pearson r between Shinobi conditions (mean across runs, per subject):")
- header = f"{'Pair':<25}" + "".join(f"{s:>12}" for s in subject_corr_mats.keys())
- header += f"{'mean_r':>10} {'range':>16}"
- print(header)
- for pair in stats_df.index:
- if pair in stats_df.index:
- row = stats_df.loc[pair]
- subj_vals = "".join(f"{row.get(s, float('nan')):>12.3f}" for s in subject_corr_mats.keys())
- label = f"{pair[0]}–{pair[1]}"
- print(f"{label:<25}{subj_vals}{row['mean_r']:>10.3f} [{row['min_r']:.3f}, {row['max_r']:.3f}]")
- # Compute global color scale
- vmin = np.min(all_values)
- vmax = np.max(all_values)
- abs_max = max(abs(vmin), abs(vmax))
- # Create 2x2 figure - compact layout
- # Top row x-labels will extend into empty upper triangle of bottom row
- fig, axes = plt.subplots(2, 2, figsize=(28, 26))
- fig.subplots_adjust(hspace=0.10, wspace=0.15)
- axes = axes.flatten()
- # Plot bottom row first (indices 2, 3), then top row (indices 0, 1)
- # This ensures top row x-labels are drawn on top of bottom row's empty space
- plot_order = [2, 3, 0, 1]
- # Plot each subject in a subplot
- for plot_idx in plot_order:
- sub_idx = plot_idx
- if sub_idx >= len(subjects[:4]):
- axes[plot_idx].set_visible(False)
- continue
- sub = subjects[sub_idx]
- ax = axes[plot_idx]
- # Set higher zorder for top row so their labels appear on top
- if plot_idx in [0, 1]:
- ax.set_zorder(10)
- if sub not in subject_corr_mats:
- ax.set_visible(False)
- continue
- corr_mat = subject_corr_mats[sub]
- # Create mask for upper triangle (including diagonal)
- mask = np.triu(np.ones_like(corr_mat, dtype=bool))
- # Plot heatmap with RdBu_r colormap (red-white-blue)
- sns.heatmap(
- corr_mat,
- mask=mask,
- center=0,
- vmin=-abs_max,
- vmax=abs_max,
- cmap='RdBu_r',
- square=True,
- linewidths=0.5,
- cbar=False, # We'll add a shared colorbar later
- ax=ax,
- xticklabels=True,
- yticklabels=True,
- annot=True, # Show correlation values in squares
- fmt='.2f', # Two decimal places
- annot_kws={'fontsize': 16} # Font size for annotations (doubled from 8)
- )
- # Make axes background transparent so top row x-labels show through
- # bottom row's masked (empty) upper triangle
- ax.set_facecolor('none')
- ax.patch.set_alpha(0)
- # Subtitle (subject name) - not bold, positioned near top of matrix
- ax.text(0.5, 0.87, f'{sub}', transform=ax.transAxes,
- fontsize=32, fontweight='normal', ha='center', va='top')
- # Get current tick labels and create display names list
- current_labels = [t.get_text() for t in ax.get_xticklabels()]
- display_labels = [display_names.get(lbl, lbl) for lbl in current_labels]
- # For lower triangle heatmap: hide first y-label and last x-label (diagonal cells)
- x_display = display_labels.copy()
- y_display = display_labels.copy()
- if len(x_display) > 0:
- x_display[-1] = '' # Last column has no visible data
- if len(y_display) > 0:
- y_display[0] = '' # First row has no visible data
- # Apply orange bold styling to all tick labels
- # ha='center' aligns rotated x-labels with their ticks
- ax.set_xticklabels(x_display, rotation=90, ha='center', va='top', fontsize=18,
- fontweight='bold', color=SHINOBI_COLOR)
- ax.set_yticklabels(y_display, rotation=0, ha='right', va='center', fontsize=18,
- fontweight='bold', color=SHINOBI_COLOR)
- # Ensure tick labels are not clipped by subplot boundaries
- ax.tick_params(axis='x', which='both', pad=2)
- ax.tick_params(axis='y', which='both', pad=2)
- for label in ax.get_xticklabels() + ax.get_yticklabels():
- label.set_clip_on(False)
- # Hide unused subplots if less than 4 subjects
- for idx in range(len(subjects[:4]), 4):
- axes[idx].set_visible(False)
- # Add shared colorbar on the right (size of one subplot, vertically centered)
- fig.subplots_adjust(right=0.92)
- # Calculate vertical center and height to match one subplot
- subplot_height = 0.35 # Approximate height of one subplot in figure coordinates
- vertical_center = 0.5 # Center of figure
- cbar_bottom = vertical_center - (subplot_height / 2)
- cbar_ax = fig.add_axes([0.94, cbar_bottom, 0.02, subplot_height])
- sm = plt.cm.ScalarMappable(
- cmap='RdBu_r',
- norm=plt.Normalize(vmin=-abs_max, vmax=abs_max)
- )
- sm.set_array([])
- cbar = fig.colorbar(sm, cax=cbar_ax)
- # Set colorbar tick label size (doubled from default)
- cbar.ax.tick_params(labelsize=20)
- # Add "Mean Pearson r" label on the right side of the colorbar (vertical)
- cbar.set_label('Mean Pearson r', fontsize=24, rotation=270, labelpad=30)
- # Save main panel directly to figures_path (not subdirectory)
- fig_fname = op.join(figures_path, f'regressor_correlations_panel{suffix}.png')
- plt.savefig(fig_fname, dpi=150, bbox_inches='tight')
- plt.close()
- if logger:
- logger.info(f"Saved 2x2 correlation plot to {fig_fname}")
- logger.summary.add_computed("2x2 subject correlation grid")
- def compute_pairwise_correlation_stats(
- subject_corr_mats: Dict[str, pd.DataFrame],
- conditions: List[str],
- ) -> pd.DataFrame:
- """
- Compute per-subject and summary statistics for all pairwise correlations
- between Shinobi conditions.
- For each unique condition pair (lower triangle), reports the per-subject
- mean Pearson r (already averaged across runs) and the range across subjects.
- Args:
- subject_corr_mats: Dict mapping subject ID -> run-averaged correlation DataFrame.
- conditions: Ordered list of condition names to include.
- Returns:
- DataFrame with rows = (condition_A, condition_B) pairs and
- columns = one column per subject + mean_r, min_r, max_r.
- """
- subjects = list(subject_corr_mats.keys())
- rows = []
- for i, cond_a in enumerate(conditions):
- for cond_b in conditions[i + 1:]:
- row: Dict[str, Any] = {'condition_A': cond_a, 'condition_B': cond_b}
- values = []
- for sub in subjects:
- mat = subject_corr_mats[sub]
- r = mat.loc[cond_a, cond_b] if (cond_a in mat.index and cond_b in mat.index) else np.nan
- row[sub] = round(float(r), 3) if not np.isnan(r) else np.nan
- if not np.isnan(r):
- values.append(r)
- row['mean_r'] = round(np.mean(values), 3) if values else np.nan
- row['min_r'] = round(np.min(values), 3) if values else np.nan
- row['max_r'] = round(np.max(values), 3) if values else np.nan
- rows.append(row)
- df = pd.DataFrame(rows).set_index(['condition_A', 'condition_B'])
- return df
- def save_correlation_stats_table(
- stats_df: pd.DataFrame,
- output_dir: str,
- suffix: str,
- logger: Optional[Any] = None,
- ) -> List[str]:
- """
- Save pairwise correlation statistics as CSV and LaTeX.
- Files written:
- - fig5_pairwise_correlation_stats{suffix}.csv
- - fig5_pairwise_correlation_stats{suffix}.tex
- Args:
- stats_df: Output of compute_pairwise_correlation_stats().
- output_dir: Directory to save tables.
- suffix: Filename suffix (e.g. '' or '_low-level').
- logger: AnalysisLogger instance.
- Returns:
- List of saved file paths.
- """
- os.makedirs(output_dir, exist_ok=True)
- stem = f"fig5_pairwise_correlation_stats{suffix}"
- csv_path = op.join(output_dir, f"{stem}.csv")
- tex_path = op.join(output_dir, f"{stem}.tex")
- stats_df.to_csv(csv_path)
- stats_df.to_latex(tex_path, float_format="%.3f", na_rep="—")
- if logger:
- logger.info(f"Pairwise correlation stats saved to {csv_path}")
- return [csv_path, tex_path]
- def main():
- """Main execution function."""
- parser = setup_argparse()
- args = parser.parse_args()
- # Setup logging
- verbosity_map = {0: logging.WARNING, 1: logging.INFO, 2: logging.DEBUG}
- verbosity_level = verbosity_map.get(args.verbose, logging.DEBUG)
- logger = AnalysisLogger(
- log_name="regressor_correlations",
- verbosity=verbosity_level,
- log_dir=args.log_dir
- )
- # Configuration
- figures_path = args.figures_path
- path_to_data = args.data_path
- subjects = [args.subject] if args.subject else config.subjects
- logger.info(f"Processing subjects: {subjects}")
- logger.info(f"Data path: {path_to_data}")
- logger.info(f"Figures path: {figures_path}")
- # Path for pickle file
- regressors_dict_fname = op.join(
- path_to_data, "processed", "regressors_dict.pkl"
- )
- # Build or load design matrices
- if args.skip_generation and op.exists(regressors_dict_fname):
- logger.info(f"Loading existing regressors from {regressors_dict_fname}")
- with open(regressors_dict_fname, "rb") as f:
- regressors_dict = pickle.load(f)
- logger.summary.add_skipped("Design matrix generation (loaded from pickle)")
- else:
- logger.info("Building design matrices...")
- if not args.exclude_low_level:
- logger.info("Including low-level confounds (psychophysics and button presses)")
- regressors_dict = build_design_matrices(
- subjects, path_to_data, figures_path,
- exclude_low_level=args.exclude_low_level,
- logger=logger
- )
- # Save to pickle
- os.makedirs(op.dirname(regressors_dict_fname), exist_ok=True)
- logger.info(f"Saving regressors to {regressors_dict_fname}")
- with open(regressors_dict_fname, "wb") as f:
- pickle.dump(regressors_dict, f)
- # Compute correlation matrices (needed for 2x2 plot)
- logger.info("Computing correlation matrices...")
- regressors_dict['corr_mat'] = []
- for run_idx, run in enumerate(regressors_dict['regressors']):
- # Compute correlation matrix (excluding constant)
- corr = run.corr()
- if 'constant' in corr.index:
- corr = corr.drop(index='constant').drop(columns='constant')
- regressors_dict['corr_mat'].append(corr)
- # Generate 2x2 subject correlation grid (Shinobi conditions + 3 psychophysics confounds only)
- logger.info("Plotting 2x2 subject correlation grid...")
- plot_2x2_subject_correlations(regressors_dict, subjects, figures_path, exclude_low_level=args.exclude_low_level, logger=logger)
- logger.info("Done!")
- logger.close()
- if __name__ == "__main__":
- main()
viz_regressor_correlations.py at commit 0f205fc, under MIT · at the source
Overview
- Department of Psychology, University of Montréal, Montréal, Canada
- Centre de Recherche de l’Institut Universitaire de Gériatrie de Montréal (CRIUGM), Montréal, Canada
- Swiss National Centre of Competence in Research, University of Geneva, Geneva, Switzerland
- IMT Atlantique, Brest, France
- Mila – Quebec AI Institute, Montréal, Canada
- UNIQUE – Union Neurosciences et Intelligence Artificielle – Québec, Québec, Canada
Abstract
In recent years, videogames have gathered interest in cognitive neuroscience for their potential to study cognition in dynamical and naturalistic contexts. Yet, the complexity of game environments often challenges traditional modeling approaches, and current annotation methods—typically manual or based on modified games—remain labor-intensive and limited in scope. Here, we introduce a flexible and scalable framework using the gym-retro Python library to emulate a classic action-platformer, Shinobi III: Return of the Ninja Master, and automatically annotate gameplay events directly from the game’s memory states. This setup enables the identification of both player actions (e.g., jumping, hitting) and feedback events (e.g., killing an enemy, being hit), without modifying the game. Four individuals played the videogame for a combined total of 32 h (>7 h each) while undergoing functional magnetic resonance imaging (fMRI). Resulting activation maps revealed distributed engagement of visual, motor, executive, and limbic systems, consistent with the cognitive demands of gameplay. Within-participant reproducibility of brain responses across sessions was robust across event types (r ≈ .25–.55), with some consistency observed even for rarer events like HealthLoss. Between-participant correlations were notably lower, reflecting participant-specific neural signatures. Multivoxel pattern analysis showed that brain responses to different in-game events were highly discriminable, with classification accuracy typically around or above 90%, though occasionally dropping to ~40% for less frequent events. These findings demonstrate that automated emulator-based annotations enable robust, interpretable, and scalable mapping of naturalistic cognitive processes using commercial videogames.
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 23 matches between paragraphs and lines of code.
courtois-neuromod/shinobi_fmri
0f205fc994d0625c45e9d38c518fea009a589f5f, 15 April 2026Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
69 files
- docs/
conf.py , Python, 244 lines - notebooks/
NB1_behav_tables.ipynb , Jupyter, 302 lines - notebooks/
NB2_replay_visualization , Jupyter, 420 lines, 1 matchs.ipynb - notebooks/
NB3_regressors_correlati , Jupyter, 157 linesons.ipynb - notebooks/
NB4_GLM_visualizations.i , Jupyter, 103 linespynb - notebooks/
NB5_beta_correlations.ip , Jupyter, 443 lines, 2 matchesynb - notebooks/
NB6_MVPA_decoding.ipynb , Jupyter, 947 lines - notebooks/
NBA1_fd_figure.ipynb , Jupyter, 275 lines - notebooks/
NBA2_behavior_figures.ip , Jupyter, 437 linesynb - setup.py, Python, 17 lines
- setup.sh, Shell, 158 lines
- shinobi_fmri/
__init__.py , Python, 1 line - shinobi_fmri/
behavioral/ , Python, 1 line__init__.py - shinobi_fmri/
behavioral/ , Python, 450 linescompute_descriptive_tabl e.py - shinobi_fmri/
behavioral/ , Python, 399 linescompute_session_skill.py - shinobi_fmri/
behavioral/ , Python, 404 linescompute_skill_metrics.py - shinobi_fmri/
behavioral/ , Python, 213 linesextract_session_timing.p y - shinobi_fmri/
config.py , Python, 115 lines - shinobi_fmri/
correlations/ , Python, 916 linescompute_beta_correlation s.py - shinobi_fmri/
correlations/ , Python, 262 linesfingerprinting_analysis. py - shinobi_fmri/
descriptive/ , Python, 6 lines__init__.py - shinobi_fmri/
descriptive/ , Python, 360 lines, 1 matchdataset_summary.py - shinobi_fmri/
glm/ , Python, 1 line__init__.py - shinobi_fmri/
glm/ , Python, 380 linesapply_cluster_correction .py - shinobi_fmri/
glm/ , Python, 522 lines, 2 matchescompute_session_level.py - shinobi_fmri/
glm/ , Python, 368 lines, 1 matchcompute_subject_level.py - shinobi_fmri/
glm/ , Python, 1,045 lines, 3 matchesutils.py - shinobi_fmri/
mvpa/ , Python, 1 line__init__.py - shinobi_fmri/
mvpa/ , Python, 162 linesaggregate_permutations.p y - shinobi_fmri/
mvpa/ , Python, 585 lines, 1 matchcompute_mvpa.py - shinobi_fmri/
utils/ , Python, 256 lineslogger.py - shinobi_fmri/
utils/ , Python, 405 linesprovenance.py - shinobi_fmri/
visualization/ , Python, 1 line__init__.py - shinobi_fmri/
visualization/ , Python, 189 lines, 1 matchhcp_tasks.py - shinobi_fmri/
visualization/ , Python, 798 linesviz_annotation_panels.py - shinobi_fmri/
visualization/ , Python, 320 linesviz_atlas_tables.py - shinobi_fmri/
visualization/ , Python, 575 lines, 2 matchesviz_beta_correlations.py - shinobi_fmri/
visualization/ , Python, 702 linesviz_between_subject_conj unction.py - shinobi_fmri/
visualization/ , Python, 270 linesviz_brain_panel.py - shinobi_fmri/
visualization/ , Python, 1,118 linesviz_condition_comparison .py - shinobi_fmri/
visualization/ , Python, 363 linesviz_descriptive_annotati ons.py - shinobi_fmri/
visualization/ , Python, 361 linesviz_descriptive_stats.py - shinobi_fmri/
visualization/ , Python, 576 linesviz_fingerprinting.py - shinobi_fmri/
visualization/ , Python, 409 linesviz_mvpa_confusion_matri ces.py - shinobi_fmri/
visualization/ , Python, 854 lines, 4 matchesviz_regressor_correlatio ns.py - shinobi_fmri/
visualization/ , Python, 312 linesviz_session_level.py - shinobi_fmri/
visualization/ , Python, 384 linesviz_session_skill_vs_rel iability.py - shinobi_fmri/
visualization/ , Python, 269 linesviz_skill_vs_correlation .py - shinobi_fmri/
visualization/ , Python, 302 linesviz_subject_level.py - shinobi_fmri/
visualization/ , Python, 1,429 lines, 1 matchviz_training_comparison. py - shinobi_fmri/
visualization/ , Python, 360 linesviz_volume_slices.py - shinobi_fmri/
visualization/ , Python, 1,077 linesviz_within_subject_corre lations.py - slurm/
batch_cancel.sh , Shell, 200 lines - slurm/
batch_launch_mvpa_permut , Python, 100 linesations.py - slurm/
subm_corrmat.sh , Shell, 33 lines - slurm/
subm_corrmat_chunk.sh , Shell, 78 lines - slurm/
subm_mvpa_aggregate.sh , Shell, 58 lines - slurm/
subm_mvpa_permutation.sh , Shell, 71 lines - slurm/
subm_mvpa_ses-level.sh , Shell, 65 lines - slurm/
subm_session-level.sh , Shell, 35 lines - slurm/
subm_subject-level.sh , Shell, 33 lines - slurm/
subm_viz-sesslevel.sh , Shell, 33 lines - slurm/
subm_viz-sub-level.sh , Shell, 33 lines - tasks.py, Python, 1,651 lines, 2 matches
- tests/
__init__.py , Python, 11 lines - tests/
utils.py , Python, 345 lines - tests/
validate_outputs.py , Python, 908 lines - LICENSE, License, 10 lines
- README.md, Text, 253 lines
Zenodo 15628556
Availability: 1 check, the latest on 27 September 2026: the link answers (HTTP 200)
- 27 September 2026: the link answers (HTTP 200)
55 files
- batch_cancel.sh, Shell, 5 lines
- docs/
conf.py , Python, 244 lines - notebooks/
NB1_behav_tables.ipynb , Jupyter, 302 lines - notebooks/
NB2_replay_visualization , Jupyter, 420 liness.ipynb - notebooks/
NB3_regressors_correlati , Jupyter, 157 linesons.ipynb - notebooks/
NB4_GLM_visualizations.i , Jupyter, 103 linespynb - notebooks/
NB5_beta_correlations.ip , Jupyter, 443 linesynb - notebooks/
NB6_MVPA_decoding.ipynb , Jupyter, 1,138 lines - notebooks/
NBA1_fd_figure.ipynb , Jupyter, 275 lines - notebooks/
NBA2_behavior_figures.ip , Jupyter, 437 linesynb - setup.py, Python, 10 lines
- shinobi_fmri/
__init__.py , Python, 1 line - shinobi_fmri/
annotations/ , Python, 1 line__init__.py - shinobi_fmri/
annotations/ , Python, 539 linesannotations.py - shinobi_fmri/
annotations/ , Python, 141 lines, 1 matchgenerate_train_json.py - shinobi_fmri/
correlations/ , Python, 198 linessession_corrmat_with_hcp .py - shinobi_fmri/
data/ , Python, 1 line__init__.py - shinobi_fmri/
data/ , Python, 107 linesdescribe_dataset.py - shinobi_fmri/
data/ , Shell, 20 linessetup_dataset.sh - shinobi_fmri/
fws/ , MATLAB, not shown heregenerate_roi_loop.mlx - shinobi_fmri/
fws/ , MATLAB, not shown heregenerate_roi_single_file .mlx - shinobi_fmri/
glm/ , Python, 1 line__init__.py - shinobi_fmri/
glm/ , Python, 469 linescompute_run_level.py - shinobi_fmri/
glm/ , Python, 595 linescompute_session_level.py - shinobi_fmri/
glm/ , Python, 105 linescompute_subject_level.py - shinobi_fmri/
glm/ , Python, 45 linesutils.py - shinobi_fmri/
mvpa/ , Python, 1 line__init__.py - shinobi_fmri/
mvpa/ , Python, 242 lines, 1 matchcompute_mvpa.py - shinobi_fmri/
visualization/ , Python, 1 line__init__.py - shinobi_fmri/
visualization/ , Python, 24 linesannotations_plot.py - shinobi_fmri/
visualization/ , Python, 143 linesfd_plot.py - shinobi_fmri/
visualization/ , Python, 180 linesregressors_correlations. py - shinobi_fmri/
visualization/ , Python, 161 linesrun_corrmat.py - shinobi_fmri/
visualization/ , Python, 33 linessession_plots.py - shinobi_fmri/
visualization/ , Python, 1 linevisualize.py - shinobi_fmri/
visualization/ , Python, 189 linesviz_run-level.py - shinobi_fmri/
visualization/ , Python, 186 linesviz_session-level.py - shinobi_fmri/
visualization/ , Python, 310 linesviz_sub-ses_plot.py - shinobi_fmri/
visualization/ , Python, 184 linesviz_subject-level.py - slurm/
batch_launch_mvpa_ses-le , Python, 9 linesvel.py - slurm/
batch_launch_run-level.p , Python, 11 linesy - slurm/
batch_launch_ses-level.p , Python, 14 linesy - slurm/
batch_launch_sub-level.p , Python, 11 linesy - slurm/
subm_corrmat.sh , Shell, 9 lines - slurm/
subm_mvpa_ses-level.sh , Shell, 9 lines - slurm/
subm_regressors_correlat , Shell, 9 linesion.sh - slurm/
subm_run-level.sh , Shell, 9 lines - slurm/
subm_script.sh , Shell, 9 lines - slurm/
subm_session-level.sh , Shell, 9 lines - slurm/
subm_subject-level.sh , Shell, 9 lines - slurm/
subm_viz-run-level.sh , Shell, 9 lines - slurm/
subm_viz-sesslevel.sh , Shell, 9 lines - slurm/
subm_viz-sub-level.sh , Shell, 9 lines - LICENSE, License, 10 lines
- README.md, Text, 4 lines
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;
- 120 scripts, each with its path and the digest of its content;
- 23 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
Datasets cited
- github.com/
courtois-neuromod/ , at github.com; found in the referencesshinobi - github.com/
courtois-neuromod/ , at github.com; found in the referencesshinobi_training
Data and Code Availability
All datasets used in this study are part of the CNeuroMod dataset (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 1, 27 September 2026: the first record
Recorded: type, language, journal, volume, pages, dates, 9 authors, 5 keywords, 4 funders, 84 references.
Cite
This paper
Harel, Y., Pinsard, B., Boyle, J., Borghesani, V., Le Clei, M., Mignot, P.-H., St-Laurent, M., Jerbi, K., & Bellec, L. (2026). Gamer in the scanner: Event-related analysis of fMRI activity during retro videogame play guided by automated annotations of game content. Imaging neuroscience (Cambridge, Mass.), 4, IMAG.a.1256. https://
BibTeX
@article{harel2026gamer,
author = {Harel, Yann and Pinsard, Basile and Boyle, Julie and Borghesani, Valentina and Le Clei, Maximilien and Mignot, Paul-Henri and St-Laurent, Marie and Jerbi, Karim and Bellec, Lune},
title = {{Gamer in the scanner: Event-related analysis of fMRI activity during retro videogame play guided by automated annotations of game content}},
journal = {Imaging neuroscience (Cambridge, Mass.)},
year = {2026},
month = jun,
volume = {4},
pages = {IMAG.a.1256},
publisher = {MIT Press},
issn = {2837-6056},
doi = {10.1162/
url = {https://
pmid = {42238756},
pmcid = {PMC13228873}
}
RIS
TY - JOUR
AU - Harel, Yann
AU - Pinsard, Basile
AU - Boyle, Julie
AU - Borghesani, Valentina
AU - Le Clei, Maximilien
AU - Mignot, Paul-Henri
AU - St-Laurent, Marie
AU - Jerbi, Karim
AU - Bellec, Lune
TI - Gamer in the scanner: Event-related analysis of fMRI activity during retro videogame play guided by automated annotations of game content
T2 - Imaging neuroscience (Cambridge, Mass.)
J2 - Imaging Neurosci (Camb)
PY - 2026
DA - 2026/
VL - 4
SP - IMAG.a.1256
SN - 2837-6056
PB - MIT Press
DO - 10.1162/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1162/
"type": "article-journal",
"title": "Gamer in the scanner: Event-related analysis of fMRI activity during retro videogame play guided by automated annotations of game content",
"container-title": "Imaging neuroscience (Cambridge, Mass.)",
"author": [
{
"family": "Harel",
"given": "Yann"
},
{
"family": "Pinsard",
"given": "Basile"
},
{
"family": "Boyle",
"given": "Julie"
},
{
"family": "Borghesani",
"given": "Valentina"
},
{
"family": "Le Clei",
"given": "Maximilien"
},
{
"family": "Mignot",
"given": "Paul-Henri"
},
{
"family": "St-Laurent",
"given": "Marie"
},
{
"family": "Jerbi",
"given": "Karim"
},
{
"family": "Bellec",
"given": "Lune"
}
],
"container-title-short":
"volume": "4",
"page": "IMAG.a.1256",
"DOI": "10.1162/
"PMID": "42238756",
"PMCID": "PMC13228873",
"ISSN": "2837-6056",
"publisher": "MIT Press",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
6,
1
]
]
}
}
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.1162/imag.a.1286 [code]
- Behavioral imitation with artificial neural networks leads to personalized models of brain dynamics during videogame play.Journal: Imaging neuroscience (Cambridge, Mass.)In common: Nilearn, Pillow, statsmodels, 8 other tools, fMRI, 14 references
- [2] doi:10.21203/rs.3.rs-9326213/v1 [code]
- Multi-task fMRI outperforms resting-state fMRI for revealing task-invariant organization of the human brainJournal: Research Square (preprint)In common: statannotations, Nilearn, Pillow, 8 other tools, fMRI, 2 references
- [3] doi:10.1038/s42003-026-10169-0 [code]
- Shared representations in brains and models reveal a two-route cortical organization during scene perception.Journal: Communications biologyIn common: Pillow, statsmodels, NiBabel, 7 other tools, 4 references
- [4] doi:10.1162/imag.a.1309 [code]
- Probing the content of semantic representations in body-selective regions.Journal: Imaging neuroscience (Cambridge, Mass.)In common: Pillow, statsmodels, NiBabel, 7 other tools, 4 references
- [5] doi:10.1162/nol.a.271 [code]
- Compositional Complexity in Text and Images.Journal: Neurobiology of language (Cambridge, Mass.)In common: Nilearn, Pillow, NiBabel, 7 other tools, fMRI, 4 references
- [6] doi:10.7554/elife.107933 [code]
- Modality-agnostic decoding of vision and language from fMRI.Journal: eLifeIn common: Nilearn, Pillow, statsmodels, 8 other tools, fMRI, 2 references
- [7] doi:10.64898/2026.08.18.26360725 [code]
- Temporal pole blurring in hippocampal sclerosis reflects seizure-disrupted myelinationJournal: medRxiv (preprint)In common: statannotations, Nilearn, Pillow, 9 other tools
- [8] doi:10.1007/s00234-026-04103-8 [code]
- Enhanced detection of subtle cortical abnormalities in focal epilepsy using 7 T MRI surface-based models and graph neural networks.Journal: NeuroradiologyIn common: statannotations, Nilearn, Pillow, 9 other tools
- [9] doi:10.1038/s41597-026-07248-6 [code]
- A large-scale fMRI dataset for vision-language semantic association.Journal: Scientific dataIn common: Nilearn, Pillow, NiBabel, 6 other tools, fMRI, 3 references
- [10] 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: statannotations, Nilearn, statsmodels, 6 other tools, fMRI, 2 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, 120 scripts, and 23 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:c099aa9293e8bd84…
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.
