origSurf = DISPLAYSURF.copy() flashSurf = pygame.Surface((BUTTONSIZE, BUTTONSIZE)) flashSurf = flashSurf.convert_alpha() r, g, b = flashColor sound.play() for start, end, step in ((0, 255, 1), (255, 0, -1)): # animation loop for alpha in range(start, end, animationSpeed * step): checkForQuit() DISPLAYSURF.blit(origSurf, (0, 0)) flashSurf.fill((r, g, b, alpha)) DISPLAYSURF.blit(flashSurf, rectangle.topleft) pygame.display.update() FPSCLOCK.tick(FPS) DISPLAYSURF.blit(origSurf, (0, 0))
The process of animating the button flash is simple: On each frame of the animation, the normal board is drawn and then on top of that, the bright color version of the button that is flashing is drawn over the button. The alpha value of the bright color starts off at
0 for the first frame of animation, but then on each frame after the alpha value is slowly increased until it is fully opaque and the bright color version completely paints over the normal button color. This will make it look like the button is slowly brightening up.
The brightening up is the first half of the animation. The second half is the button dimming. This is done with the same code, except that instead of the alpha value increasing for each frame, it will be decreasing. As the alpha value gets lower and lower, the bright color painted on top will become more and more invisible, until only the original board with the dull colors is visible.
To do this in code, line 1  creates a copy of the display Surface object and stores it in
origSurf. Line 2  creates a new Surface object the size of a single button and stores it in
convert_alpha() method is called on
flashSurf so that the Surface object can have transparent colors drawn on it (otherwise, the alpha value in the Color objects we use will be ignored and automatically assumed to be 255). In your own game programs, if you are having trouble getting color transparency to work, make sure that you have called the
convert_alpha() method on any Surface objects that have transparent colors painted on them.
Line 4  creates individual local variables named
b to store the individual RGB values of the tuple stored in
flashColor. This is just some syntactic sugar that makes the rest of the code in this function easier to read. Before we begin animating the button flash, line 5  will play the sound effect for that button. The program execution keeps going after the sound effect has started to play, so the sound will be playing during the button flash animation.
Remember that to do the animation, we want to first draw the
flashSurf with color that has increasing alpha values from
255 to do the brightening part of the animation. Then to do the dimming, we want the alpha value to go from
0. We could do that with code like this:
for alpha in range(0, 255, animationSpeed): # brightening checkForQuit() DISPLAYSURF.blit(origSurf, (0, 0)) flashSurf.fill((r, g, b, alpha)) DISPLAYSURF.blit(flashSurf, rectangle.topleft) pygame.display.update() FPSCLOCK.tick(FPS) for alpha in range(255, 0, -animationSpeed): # dimming checkForQuit() DISPLAYSURF.blit(origSurf, (0, 0)) flashSurf.fill((r, g, b, alpha)) DISPLAYSURF.blit(flashSurf, rectangle.topleft) pygame.display.update() FPSCLOCK.tick(FPS)
But notice that the code inside the
for loops handles drawing the frame and are identical to each other. If we wrote the code like the above, then the first
for loop would handle the brightening part of the animation (where the alpha value goes from
255) and the second
for loop would handle the dimming part of the animation (where the alpha values goes from
0). Note that for the second for loop, the third argument to the
range() call is a negative number.
Whenever we have identical code like this, we can probably shorten our code so we don’t have to repeat it. This is what we do with the
for loop on line 6 , which supplies different values for the
range() call on line 7 .
On the first iteration of line 6's  for loop,
start is set to
end is set to
step is set to
1. This way, when the for loop on line 7  is executed, it is calling
range(0, 255, animationSpeed). (Note that
animationSpeed * 1 is the same as
animationSpeed. Multiplying a number by
1 gives us the same number.)
Line 7's 
for loop then executes and performs the brightening animation.
On the second iteration of line 6's 
for loop (there are always two and only two iterations of this inner
start is set to
end is set to
step is set to
-1. When the line 7's 
for loop is executed, it is calling
range(255, 0, -animationSpeed). (Note that
animationSpeed * -1 evaluates to
-animationSpeed, since multiplying any number by
-1 returns the negative form of that same number.)
This way, we don’t have to have two separate
for loops and repeat all the code that is inside of them. Here’s the code again that is inside line 7's 
checkForQuit() DISPLAYSURF.blit(origSurf, (0, 0)) flashSurf.fill((r, g, b, alpha)) DISPLAYSURF.blit(flashSurf, rectangle.topleft) pygame.display.update() FPSCLOCK.tick(FPS) DISPLAYSURF.blit(origSurf, (0, 0))
We check for any
QUIT events (in case the user tried to close the program during the animation), then blit the
origSurf Surface to the display Surface. Then we paint the
flashSurf Surface by calling
fill() (supplying the
b values of the color we got on line 4  and the alpha value that the
for loop sets in the
alpha variable). Then the
flashSurf Surface is blitted to the display Surface.
Then, to make the display Surface appear on the screen,
pygame.display.update() is called on line 12 . To make sure the animation doesn’t play as fast as the computer can draw it, we add short pauses with a call to the
tick() method. (If you want to see the flashing animation play very slowly, put a low number like 1 or 2 as the argument to
tick() instead of