Issues generating a precisely timed expanding circle

I’ve been trying to write a simple script that generates an expanding circle that:

  1. Expands from 2 to 20 degrees of visual angle over 250 ms.

  2. Holds at 20 degrees for another 250 ms.

  3. Repeats n number of times with 500 ms inter-stimulus interval (blank, grey screen).

I think I have the code working. However, 1) I think there must be a simpler/better way of doing this and 2) when I run the code, although no frame drops are reported, there appears to be some lag/variation in expansion between iterations.

If anyone could comment on either of these points I would appreciate it.

Code so far:


from psychopy import visual, core, event

# Define the background
win=visual.Window([800,600],monitor="testMonitor",units="deg")

# Number of epochs (repetitions)
EPOCH=15

# Duration of expansion
EXPANSION_MS=250

# Hold duration
HOLD_TIME_MS=250

# ISI duration
ISI_MS=500

# Time-to-frame conversion assuming a 60Hz FR and RR
FRAMES=int((60*EXPANSION_MS)/1000)
HOLD_FRAMES=int((60*HOLD_TIME_MS)/1000)
ISI_FRAMES=int((60*ISI_MS)/1000)
print(FRAMES)

# Define the stimulus
circle=visual.Circle(win,radius=1,edges=1000,fillColor='black',lineColor='black')

for e in range(EPOCH):
    for f in range(FRAMES):
        if circle.radius<10:
            circle.radius+=1 # or should it be: circle.radius=(circle.radius+(9/FRAMES))?
            circle.draw()
            win.flip()
    for f in range(HOLD_FRAMES):
        circle.radius=10
        circle.draw()
        win.flip()
    for f in range(ISI_FRAMES):
        win.flip()
    print(circle.radius)
    circle.radius=1

# Get keys to quit.
if len(event.getKeys())>0:
    event.clearEvents()
    win.close()




HI @dylanablack, I would suggest you may be better off using Builder, as it is easily put together. You will need one routines in a loop, where loop controls number of presentations. In the routine. use a static component starting at zero time, with a duration of 500ms, which will be your ISI. Then use a Polygon component, where the shape is a regular polygon with 99 sides - this will give you your circle - remember to set units to degrees. Give your polygon a start of .5 seconds, and a duration of .5 seconds, set the size to circleSize and get it to update every screen refresh. To define and update yourcircleSize variable, we use a code component in Builder. For setting sizes, we will think in terms of frames, or screen refreshes, where one frame is 1/60hz refresh rate = 16.67ms per frame.

In your code component, add the following to the relevant tabs:

# Begin Routine
expansionFrames = 15  # duration of your expansion in frames, or 250ms / 16.67
expansionDegrees = 18 # starting at 2 degrees, 2 + 18 = 20 degrees
expansionRate = expansionDegrees / expansionFrames # expand 1.2 degrees per frame
circleSize = 2  # starting size in degrees

# Each frame
if polygon.size[0] < 20 - 1:  # if not already 20 degrees, expand.
    circleSize += expansionRate

The circle will expand 1.2 degree every frame, and it will take 15 frames to reach 18 degrees of expansion, + starting size = 20 degrees