Simple Pygame animation stuttering
<p>I'm learning python <code>*\(^o^)/*</code></p>

<p>I have a simple bouncing box window drawn using <code>Pygame</code>. Everything seems to be functioning properly, except for one minor annoyance. It stutters constantly! I have no idea what could be causing the stutter. I thought it might be lag, so I implemented a fixed time-step to allow the loop to catch up, but this had no effect. </p>

<pre><code>#--- initialize pygame window ---#
import pygame
import time
pygame.init()
size = (1200,500)
screen = pygame.display.set_mode(size, pygame.RESIZABLE)
fps = 60

#--- define color palette ---#
black = (0,0,0)
white = (255,255,255)

#--- define the player ---#
class player:
    def __init__(self,screen,surface, color):
        self.speed = 3
        self.direction_x = 1
        self.direction_y = 1
        self.screen = screen
        self.surface = surface
        self.rect = self.surface.get_rect()
        self.color = color
    def set_pos(self, x,y):
        self.rect.x = x
        self.rect.y = y
    def advance_pos(self):
        screen_width, screen_height = screen.get_size()
        if self.rect.x + self.rect.width &gt; screen_width or player1.rect.x &lt; 0:
            player1.direction_x *= -1
            player1.speed = 3
        elif player1.rect.y + player1.rect.height &gt; screen_height or player1.rect.y &lt; 0:
            player1.direction_y *= -1
            player1.speed = 3
        else:
            player1.speed -= 0.001
        self.rect.x += self.speed * self.direction_x
        self.rect.y += self.speed * self.direction_y
    def draw(self):
        pygame.draw.rect(self.surface, self.color, [0,0,self.rect.width,self.rect.height])
    def blit(self):
        screen.blit(self.surface, self.rect)
player1 = player(screen, pygame.Surface((50,50)), white)
player1.set_pos(50,50)
player1.draw()

#--- define game variables ---#
previous = time.time() * 1000
lag = 0.0
background = black
done = False

#--- game ---#
while not done:

    #--- update time step ---#
    current = time.time() * 1000
    elapsed = current - previous
    lag += elapsed
    previous = current

    #--- process events ---#
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
            break
        if event.type == pygame.VIDEORESIZE:
            screen = pygame.display.set_mode((event.w, event.h), pygame.RESIZABLE)

    #--- update logic ---#

    while True:
        player1.advance_pos()
        lag -= fps
        if lag &lt;= fps:
            break

    #--- draw to screen ---#
    screen.fill(background)
    player1.blit()
    pygame.display.update()
    pygame.time.Clock().tick(fps)
</code></pre>

<p>This is a rewrite of your code that uses opengl instead for the rendering. The major changes are as follows:</p>

<ol>
<li>I used opengl immediate mode, which is out-of-date and deprecated, but is a lot easier to understand at first. Most of the gl calls are either in the player.draw() method or in the main loop.</li>
<li><p>I fixed the way the timer is done. Rather than doing just clock.tick(fps), I manually keep track of the amount of time that it takes to do all of the processing to the frame and add the appropriate millisecond delay to reach 60 fps. You can try that modification with your existing pygame code before migrating to opengl as that might be sufficient to remove most of the stutter.</p>

<pre><code>import pygame
import time
from OpenGL.GL import *

class Player:
    def __init__(self, screen, width, height, color):
        self.x = 0
        self.y = 0
        self.speed = 3
        self.direction_x = 1
        self.direction_y = 1
        self.screen = screen
        self.width = width
        self.height = height
        self.color = color

    def set_pos(self, x, y):
        self.x = x
        self.y = y

    def advance_pos(self):
        screen_width, screen_height = screen.get_size()
        if self.x + self.width &gt; screen_width or self.x &lt; 0:
            self.direction_x *= -1
            self.speed = 3
        elif self.y + self.height &gt; screen_height or self.y &lt; 0:
            self.direction_y *= -1
            self.speed = 3
        else:
            self.speed -= 0.001
        self.x += self.speed * self.direction_x
        self.y += self.speed * self.direction_y

    def draw(self):
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        glTranslate(self.x, self.y, 0)
        glBegin(GL_QUADS)
        glColor(*self.color)
        glVertex(0, 0, 0)
        glVertex(self.width, 0, 0)
        glVertex(self.width, self.height, 0)
        glVertex(0, self.height, 0)
        glEnd()

if __name__ == "__main__":
    pygame.init()
    size = width, height = (550, 400)
    screen = pygame.display.set_mode(size, pygame.RESIZABLE | pygame.DOUBLEBUF | pygame.OPENGL)
    fps = 60
    black = (0,0,0,255)
    white = (255,255,255,255)

    player1 = Player(screen, 50, 50, white)
    player1.set_pos(50,50)

    done = False
    previous = time.time() * 1000
    glClearColor(*black)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    glOrtho(0, width, height, 0, -1, 1)
    clock = pygame.time.Clock()

    while not done:
        current = time.time() * 1000
        elapsed = current - previous
        previous = current
        delay = 1000.0/fps - elapsed
        delay = max(int(delay), 0)

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                done = True
                break
            if event.type == pygame.VIDEORESIZE:
                size = width, height = event.w, event.h
                screen = pygame.display.set_mode(size, pygame.RESIZABLE | pygame.DOUBLEBUF | pygame.OPENGL)
                glMatrixMode(GL_PROJECTION)
                glLoadIdentity()
                glOrtho(0, width, height, 0, -1, 1)
                glViewport(0, 0, width, height)

                #reset player movement and position to avoid glitches where player is trapped outside new window borders
                player1.set_pos(50, 50)
                player1.direction_x = 1
                player1.direction_y = 1

        player1.advance_pos()
        glClear(GL_COLOR_BUFFER_BIT)
        glClear(GL_DEPTH_BUFFER_BIT)
        player1.draw()
        pygame.display.flip()
        pygame.time.delay(delay)
</code></pre></li>
</ol>

python pygame