Black flash/flicker (black rectangle) at onset of MovieStim playback

Hello everyone,

I am running an experiment where I present a “Learning Block” consisting of: Fixation → Cue Clip → (Gray Screen Gap) → Target Clip.

The Problem: I consistently see a tiny “jump” or flicker (a black rectangle the size of the video element) at two specific moments:

  1. Immediately after the Fixation, right before the “Cue” video starts.

  2. Immediately after the “Cue” video finishes, right before the “Target” video starts.

My Setup:

  • Background: Gray (RGB = 0, 0, 0 in ‘rgb’ color space).

  • Stimuli: Short video clips using visual.MovieStim.

What I have tried:

  • Using movie.seek(0) followed by movie.draw() before the loop.

  • Adding delays with core.wait().

  • Explicitly drawing a gray fill between the movies, to mask the black one.

  • Reduce the files size.

None of these have eliminated the black frame flash at the onset of the videos.

I attached the relevant function.

Does anyone know how to solve it?

Any help is appreciated!


def run_combined_block_session(win, blocks, block_idx, subj_info, exp_info, clock,diode_cue, fixation, gray_fill,test_instr_dir, test_instr_files,ques_instr_dir, ques_instr_files,math_instr_dir, math_instr_files,screen_size):

# — SETUP —
b = block_idx
block_df = blocks[b]
current_date = datetime.now().strftime(‘%Y-%m-%d’)
stim_hours = {}
stim_times = {}

\# ---------------------------------------------------------
\# 0: SHOW LOADING SCREEN IMMEDIATELY
\# ---------------------------------------------------------
lang = subj_info.get('Language', 'e').lower()

\# Defaults
load_str = "Loading next block...\\nPlease wait."
style_to_use = 'LTR'
text_font = 'Arial'
wrap_w = exp_info.info\['monitor_x_pix'\]


loading_text = visual.TextStim(win, text=load_str,
                               font=text_font,
                               units='pix', height=exp_info.info\['text_size'\],
                               color=exp_info.info\['text_color'\],
                               languageStyle=style_to_use,
                               wrapWidth=wrap_w)

loading_text.draw()
win.flip()

\# ---------------------------------------------------------
\# 1. PRELOAD STIMULI
\# ---------------------------------------------------------
print(f"--- Preloading media for Block {b + 1} ---")

movie_list = \[str(p).strip() for p in block_df\['movie_path'\].tolist()\]
cue_list = \[str(p).strip() for p in block_df\['cue_path'\].tolist()\]
cue_set = set(cue_list)
unique_paths = set(movie_list + cue_list)

media_buffer = {}

for vid_path in unique_paths:
    if vid_path and str(vid_path) != 'nan' and os.path.exists(str(vid_path)):
        try:
            if vid_path in cue_set:
                vid_size = (960, 540)
            else:
                vid_size = (exp_info.info\['stim_x_pix'\], exp_info.info\['stim_y_pix'\])

            mov = visual.MovieStim(win, filename=str(vid_path),
                                   units='pix',
                                   size=vid_size,
                                   pos=(0, 0),
                                   flipHoriz=False, flipVert=False,
                                   loop=False,
                                   autoStart=False)
            media_buffer\[str(vid_path)\] = mov
            print(f"Loaded: {os.path.basename(vid_path)}")
        except Exception as e:
            print(f"Error loading {vid_path}: {e}")
    else:
        print(f"CRITICAL WARNING: Video not found: {vid_path}")

print(f"--- Finished Loading {len(media_buffer)} unique videos ---")
win.frameIntervals = \[\]
win.recordFrameIntervals = True

\# ---------------------------------------------------------
\# 2. LEARNING PHASE
\# ---------------------------------------------------------
block_log_data = \[\]

location_cue_stim = visual.TextStim(win, text='',
                                    units='pix', height=exp_info.info\['text_size'\],
                                    pos=(0, 350), color=exp_info.info\['text_color'\],
                                    languageStyle='arabic')

stim_hours\[f'block\_{b + 1}\_start'\] = datetime.now().strftime("%H:%M:%S:%f")

\# Flag to skip learning phase if 't' is pressed
skip_learning = False

for i in range(len(block_df)):
    \# Check if we need to skip the rest of the learning phase
    if skip_learning:
        break

    movie_path = str(block_df.iloc\[i\]\['movie_path'\]).strip()
    cue_path = str(block_df.iloc\[i\]\['cue_path'\]).strip()
    location_col_name = f"Where_space\_{subj_info\['Language'\]}"
    location_text = str(block_df.iloc\[i\]\[location_col_name\])

    if movie_path in media_buffer and cue_path in media_buffer:
        movie = media_buffer\[movie_path\]
        cue = media_buffer\[cue_path\]
        cue.seek(0)
    else:
        print(f"Error: Missing media for trial {i}. Skipping.")
        continue

    location_cue_stim.setText(location_text)

    \# --- CUE SEQUENCE ---
    if i == 0:
        fixation.draw()
        win.flip()
        core.wait(exp_info.info\['block_start_flash'\])

    gray_fill.draw()
    win.flip()

    cue.play()
    \# \[NEW\] LEARNING PHASE: DIODE AT START OF CUE CLIP
    for \_ in range(exp_info.info\['clip_onset_flash'\]):
        cue.draw()
        location_cue_stim.draw()
        diode_cue.draw()
        win.flip()

    while not cue.isFinished:
        cue.draw()
        location_cue_stim.draw()
        win.flip()

        \# Check for keys: Escape, Test (t), Next Block (n)
        keys = event.getKeys(keyList=\['escape', 't', 'n'\])
        if 'escape' in keys:
            win.close()
            core.quit()
        elif 't' in keys:
            cue.stop()
            skip_learning = True
            break  # Break inner loop
        elif 'n' in keys:
            cue.stop()
            return  # Exit function immediately (Next Block)

    \# If 't' was pressed, break the main loop too
    if skip_learning:
        break

    cue.stop()
    event.clearEvents()

    core.wait(exp_info.info\['grey_fill_time'\])

    \# --- MOVIE SEQUENCE ---
    movie.seek(0)

    if movie.status == visual.FINISHED:
        movie.stop()

    gray_fill.draw()
    win.flip()

    #core.wait(exp_info.info\['grey_fill_time'\])
    stim_hours\[f'block\_{b + 1}\_clip\_{i + 1}\_start'\] = datetime.now().strftime("%H:%M:%S:%f")
    movie.play()

    \# \[NEW\] LEARNING PHASE: DIODE AT START OF CLIP
    for \_ in range(exp_info.info\['clip_onset_flash'\]):
        movie.draw()
        diode_cue.draw()
        win.flip()

    while not movie.isFinished:
        movie.draw()
        win.flip()

        \# Check for keys: Escape, Test (t), Next Block (n)
        keys = event.getKeys(keyList=\['escape', 't', 'n'\])
        if 'escape' in keys:
            win.close()
            core.quit()
        elif 't' in keys:
            movie.stop()
            skip_learning = True
            break  # Break inner loop
        elif 'n' in keys:
            movie.stop()
            return  # Exit function immediately (Next Block)

    \# If 't' was pressed, break the main loop too
    if skip_learning:
        break

    stim_hours\[f'block\_{b + 1}\_clip\_{i + 1}\_end'\] = datetime.now().strftime("%H:%M:%S:%f")

    
    \# Note: We draw fixation AND diode to signal end
    fixation.draw()
    diode_cue.draw()
    win.flip()
    core.wait(exp_info.info\['clip_offset_flash'\])

    movie.stop()

    block_log_data.append({
        'ID': block_df.iloc\[i\]\['ID'\],
        'BlockID': block_df.iloc\[i\]\['BlockID'\],
        'BlockIndex': b,
        'TrialIndex': i,
        'cb_HMO20': block_df.iloc\[i\]\['cb_HMO20'\],
        'clip_Start_H': stim_hours\[f'block\_{b + 1}\_clip\_{i + 1}\_start'\],
        'clip_End_H': stim_hours\[f'block\_{b + 1}\_clip\_{i + 1}\_end'\]
    })

    gray_fill.draw()
    win.flip()
    core.wait(exp_info.info\['grey_fill_time'\])

\# Save Learning Log
block_df_log = pd.DataFrame(block_log_data)
block_log_filename = ('log_files/' + 'sub' + str(subj_info\['Subject Number'\]) +
                      f'\_log_block\_{b + 1}\_{current_date}.csv')
block_df_log.to_csv(block_log_filename, index=False)
stim_hours\[f'block\_{b + 1}\_end'\] = datetime.now().strftime("%H:%M:%S:%f")

\# ---------------------------------------------------------
\# 3. INTERMISSION: TEST INSTRUCTIONS
\# ---------------------------------------------------------

run_div3_distractor(
    win,
    subj_info=subj_info,
    duration=45.0,
    text_color=exp_info.info\['text_color'\],
    instr_dir=math_instr_dir,
    instr_files=math_instr_files,
    screen_size=screen_size
)

stim_hours\[f'block\_{b + 1}\_test_break_start'\] = datetime.now().strftime("%H:%M:%S:%f")
event.clearEvents()
show_instruction_slides(win, test_instr_dir, test_instr_files, screen_size)
stim_hours\[f'block\_{b + 1}\_test_break_end'\] = datetime.now().strftime("%H:%M:%S:%f")

\# ---------------------------------------------------------
\# 4. TEST PHASE
\# ---------------------------------------------------------
test_log_data = \[\]

current_block_id = block_df\['BlockID'\].iloc\[0\]
test_order_indices = exp_info.test_orders\[current_block_id\]
max_idx = len(block_df)
test_order_indices = \[idx for idx in test_order_indices if idx < max_idx\]

location_col_name = f"Where_space\_{subj_info\['Language'\]}"

for test_i, df_row_idx in enumerate(test_order_indices):
    trial_data = block_df.iloc\[df_row_idx\]
    cue_path = str(trial_data\['cue_path'\]).strip()
    location_text = str(trial_data\[location_col_name\])

    if cue_path in media_buffer:
        movie_cue = media_buffer\[cue_path\]
        movie_cue.seek(0)
    else:
        print(f"Error: Cue video missing for test trial {test_i}")
        continue

    location_cue_stim.setText(location_text)

    stim_hours\[f'test\_{b}\_{df_row_idx}\_start'\] = datetime.now().strftime("%H:%M:%S:%f")
    stim_times\[f'test\_{b}\_{df_row_idx}\_start'\] = clock.getTime()

    fixation.draw()
    win.flip()
    core.wait(exp_info.info\['block_start_flash'\])

    \# NOTE: Previously this part flashed only text.
    \# Now, we start the movie immediately to flash Diode + Movie.

    gray_fill.draw()
    win.flip()

    movie_cue.draw()
    location_cue_stim.draw()
    win.flip()

    movie_cue.play()

    \# \[NEW\] TEST PHASE: DIODE AT BEGINNING OF SCREEN (MOVIE START)
    for \_ in range(exp_info.info\['clip_onset_flash'\]):
        movie_cue.draw()
        location_cue_stim.draw()
        diode_cue.draw()
        win.flip()

    while not movie_cue.isFinished:
        movie_cue.draw()
        location_cue_stim.draw()
        win.flip()

        \# Check for keys: Escape, Next Block (n)
        keys = event.getKeys(keyList=\['escape', 'n'\])
        if 'escape' in keys:
            win.close()
            core.quit()
        elif 'n' in keys:
            movie_cue.stop()
            return  # Exit function immediately (Next Block)

    \# \[NEW\] TEST PHASE: FREEZE FRAME UNTIL ENTER
    movie_cue.pause()  # Freezes at current frame (end)

    waiting_for_enter = True
    event.clearEvents()  # Clear old keys

    while waiting_for_enter:
        \# Draw static last frame
        movie_cue.draw()
        location_cue_stim.draw()
        win.flip()

        keys = event.getKeys(keyList=\['return', 'escape', 'n'\])

        if 'return' in keys:
            \# \[NEW\] TEST PHASE: DIODE WHEN PATIENT PRESSES ENTER
            \# Flash the diode while keeping the frame
            for \_ in range(exp_info.info\['clip_onset_flash'\]):
                movie_cue.draw()
                location_cue_stim.draw()
                diode_cue.draw()
                win.flip()
            waiting_for_enter = False

        elif 'escape' in keys:
            win.close()
            core.quit()
        elif 'n' in keys:
            movie_cue.stop()
            return

    movie_cue.stop()

    event.clearEvents()
    show_instruction_slides(win, ques_instr_dir, ques_instr_files, screen_size)

    stim_times\[f'test\_{b}\_{df_row_idx}\_end'\] = clock.getTime()
    stim_hours\[f'test\_{b}\_{df_row_idx}\_end'\] = datetime.now().strftime("%H:%M:%S:%f")

    test_log_data.append({
        'block': b,
        'trial': test_i,
        'df_row_idx': df_row_idx,
        'cue_path': cue_path,
        'location_text': location_text
    })

test_log_df = pd.DataFrame(test_log_data)
current_hour = datetime.now().strftime("%H-%M-%S")
test_log_filename = ('log_files/' + 'sub' + str(subj_info\['Subject Number'\]) +
                     f'\_test_log_block\_{b + 1}\_{current_date}\_h\_{current_hour}.csv')
test_log_df.to_csv(test_log_filename)

time_log_filename = ('log_files_hour/' + 'sub' + str(subj_info\['Subject Number'\]) +
                     f'\_time_log_block\_{b + 1}\_{current_date}\_h\_{current_hour}.csv')
pd.DataFrame(\[stim_hours\]).to_csv(time_log_filename)
return

I think this is the same bug as in another thread so here’s my reply to that one:

1 Like

Thank you!