Docs › game library

game.frame()

The heartbeat of every game. game.frame() draws one picture and waits a moment, and game.playing() keeps the loop going until the game ends. Together they make things move.

How to write it

while game.playing(): ... game.frame(fps=30)
fps
Optional. Frames per second: how many pictures to draw each second. Leave it out and you get 30. Try 60 for extra-smooth motion. Decimals are rounded down, and the smallest it goes is 1.

game.frame() gives nothing back. game.playing() takes nothing and gives back True while the game is running, and False once it has ended.

A full example

A rocket flies across the screen and wraps round to the other side. Press space to switch between 30 and 60 frames a second, and watch what happens to its speed:

import game

game.window(480, 360, background="#0b1020")

rocket = game.sprite("rocket", 0, 180, size=50)   # it points right
info = game.label("30 fps", 240, 24, color="#ffffff")

fps = 30
step = 6                          # pixels per frame
was_down = False

while game.playing():
    # Space switches between 30 and 60 fps, once per press
    down = game.pressed("space")
    if down and not was_down:
        if fps == 30:
            fps = 60
            step = 3              # half the step, so the speed stays the same
        else:
            fps = 30
            step = 6
        info.content = str(fps) + " fps"
    was_down = down

    rocket.x = rocket.x + step
    if rocket.x > 500:
        rocket.x = -20            # off the right edge? come back on the left

    game.frame(fps)

The rocket crosses the screen in the same time at 30 and at 60 fps, because the step is halved when the frames double. At 60 it just looks smoother. Try deleting the two step = lines inside the if and see it zoom off at double speed.

Why every game needs this loop

A game is like a flip-book. Each time round the loop you change things a tiny bit (move the player, check for a crash), then game.frame() shows the new picture. Do that 30 times a second and it looks like smooth motion.

  • game.frame() draws. Sprites only appear on the screen when it runs. Changing hero.x does nothing you can see until the next frame.
  • game.frame() waits. After drawing, it pauses for a moment (1/30 of a second by default). That pause gives the browser time to read the keys and the mouse, and to keep the page working.
  • game.playing() knows when to stop. It turns False when you press Stop or when game.game_over() ends the game, so the loop finishes cleanly.
  • Put game.frame() last, at the bottom of the loop, after all your moving and checking.

Code above the loop runs once, to set things up. Code inside the loop runs every frame. Code after the loop runs once the game has ended.

Speed and fps

How fast something moves depends on two numbers: how far it moves each frame, and how many frames there are each second.

  • 6 pixels a frame at 30 fps is 6 × 30 = 180 pixels a second.
  • 6 pixels a frame at 60 fps is 6 × 60 = 360 pixels a second: twice as fast!
  • 3 pixels a frame at 60 fps is back to 180 pixels a second, but smoother.

So if you switch to game.frame(60), halve your step sizes. A low number like game.frame(10) makes a jerky, retro look, good for a snake game that moves one square at a time.

The fps is a target, not a promise. A game doing lots of work each frame runs a little slower than the number you ask for, so never use frames to time something exactly to the second.

Why isn't my game loop working?

  • The page froze. The loop has no game.frame(), so it never pauses and the browser gets no chance to do anything else. Close or reload the tab, then add game.frame() as the last line inside the loop.
  • The window is empty. There is no loop, or no game.frame(), so nothing is ever drawn.
  • I did write game.frame(), but it still froze. Check the indent. If game.frame() is not pushed in like the other lines in the loop, it sits after the loop and never runs. It must line up with the lines inside.
  • It runs super slowly. Check for game.frame(1) or game.frame(0): anything below 1 counts as 1, which is one picture a second.
  • Two loops, and the second never starts. The first while game.playing(): only ends when the game has ended, and then game.playing() is False, so a second loop after it never runs at all. Use one loop, with an if inside to switch between a menu and the game.