ShapeElementList Explanation
If you are drawing a lot of items on your screen, the arcade.ShapeElementList
can
speed your drawing. How does it work?
Say we have a screen with about 9,600 rectangles:

Those rectangles are created with 38,400 points + 9,600 colors + 9,600 angles. They need to be sent to the graphics card 60 times per second. This quickly balloons into 3.5 million numbers being sent to the graphics card each second to display a bunch of rectangles that aren’t even moving.
What we want to do instead is package all these rectangles up, send them to the graphics card, and then have them be drawn in just one command. This takes us from 3.5 million items per second down to about 60.
There are three examples here:
First, no shape lists. Just drawing rectangles.
Second, each rectangle is individually buffered to the GPU.
Third, all rectangles are buffered to the GPU as and drawn as a group.

Example One - No Shape Lists
1"""
2This demo shows the speed of drawing a full grid of squares using no buffering.
3
4For me this takes about 0.16 seconds per frame.
5
6It is slow because we load all the points and all the colors to the card every
7time.
8
9If Python and Arcade are installed, this example can be run from the command line with:
10python -m arcade.examples.shape_list_demo_1
11"""
12
13import arcade
14import timeit
15
16WINDOW_WIDTH = 1200
17WINDOW_HEIGHT = 800
18WINDOW_TITLE = "Shape List Demo 1"
19
20SQUARE_WIDTH = 5
21SQUARE_HEIGHT = 5
22SQUARE_SPACING = 10
23
24
25class GameView(arcade.View):
26 """ Main application class. """
27
28 def __init__(self):
29 super().__init__()
30
31 self.background_color = arcade.color.DARK_SLATE_GRAY
32
33 self.draw_time = 0
34
35 def on_draw(self):
36 """
37 Render the screen.
38 """
39
40 # This command has to happen before we start drawing
41 self.clear()
42
43 # Start timing how long this takes
44 draw_start_time = timeit.default_timer()
45
46 # --- Draw all the rectangles
47 for x in range(0, WINDOW_WIDTH, SQUARE_SPACING):
48 for y in range(0, WINDOW_HEIGHT, SQUARE_SPACING):
49 arcade.draw_rect_filled(arcade.rect.XYWH(x, y, SQUARE_WIDTH, SQUARE_HEIGHT),
50 arcade.color.DARK_BLUE)
51
52 # Print the timing
53 output = f"Drawing time: {self.draw_time:.3f} seconds per frame."
54 arcade.draw_text(output, 20, WINDOW_HEIGHT - 40, arcade.color.WHITE, 18)
55
56 self.draw_time = timeit.default_timer() - draw_start_time
57
58
59def main():
60 """ Main function """
61 # Create a window class. This is what actually shows up on screen
62 window = arcade.Window(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE)
63
64 # Create the GameView
65 game = GameView()
66
67 # Show GameView on screen
68 window.show_view(game)
69
70 # Start the arcade game loop
71 arcade.run()
72
73
74if __name__ == "__main__":
75 main()
Example Two - Simple Shape Lists
1"""
2This demo shows using buffered rectangles to draw a grid of squares on the
3screen.
4
5For me this runs about 0.002 seconds per frame.
6
7It is faster than demo 1 because we aren't loading the vertices and color
8to the card again and again. It could be faster though, if we group all
9rectangles together.
10
11If Python and Arcade are installed, this example can be run from the command line with:
12python -m arcade.examples.shape_list_demo_2
13"""
14
15import arcade
16import timeit
17
18WINDOW_WIDTH = 1200
19WINDOW_HEIGHT = 800
20WINDOW_TITLE = "Shape List Demo 2"
21
22SQUARE_WIDTH = 5
23SQUARE_HEIGHT = 5
24SQUARE_SPACING = 10
25
26
27class GameView(arcade.View):
28 """ Main application class. """
29
30 def __init__(self):
31 super().__init__()
32
33 self.background_color = arcade.color.DARK_SLATE_GRAY
34
35 self.draw_time = 0
36 self.shape_list = None
37
38 def setup(self):
39 # --- Create the vertex buffers objects for each square before we do
40 # any drawing.
41 self.shape_list = arcade.shape_list.ShapeElementList()
42 for x in range(0, WINDOW_WIDTH, SQUARE_SPACING):
43 for y in range(0, WINDOW_HEIGHT, SQUARE_SPACING):
44 shape = arcade.shape_list.create_rectangle_filled(
45 center_x=x,
46 center_y=y,
47 width=SQUARE_WIDTH,
48 height=SQUARE_HEIGHT,
49 color=arcade.color.DARK_BLUE,
50 )
51 self.shape_list.append(shape)
52
53 def on_draw(self):
54 """
55 Render the screen.
56 """
57
58 # This command has to happen before we start drawing
59 self.clear()
60
61 # Start timing how long this takes
62 draw_start_time = timeit.default_timer()
63
64 # --- Draw all the rectangles
65 self.shape_list.draw()
66
67 output = f"Drawing time: {self.draw_time:.3f} seconds per frame."
68 arcade.draw_text(output, 20, WINDOW_HEIGHT - 40, arcade.color.WHITE, 18)
69
70 self.draw_time = timeit.default_timer() - draw_start_time
71
72
73def main():
74 """ Main function """
75 # Create a window class. This is what actually shows up on screen
76 window = arcade.Window(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE)
77
78 # Create and setup the GameView
79 game = GameView()
80 game.setup()
81
82 # Show GameView on screen
83 window.show_view(game)
84
85 # Start the arcade game loop
86 arcade.run()
87
88
89if __name__ == "__main__":
90 main()
Example Three - Complex Shape Lists
1"""
2This demo shows drawing a grid of squares using a single buffer.
3
4We calculate the points of each rectangle and add them to a point list.
5We create a list of colors for each point.
6We then draw all the squares with one drawing command.
7
8This runs in about 0.000 seconds for me. It is much more complex in code
9than the prior two examples, but the pay-off in speed is huge.
10
11If Python and Arcade are installed, this example can be run from the command line with:
12python -m arcade.examples.shape_list_demo_3
13"""
14
15import arcade
16import timeit
17
18WINDOW_WIDTH = 1200
19WINDOW_HEIGHT = 800
20WINDOW_TITLE = "Shape List Demo 3"
21
22HALF_SQUARE_WIDTH = 2.5
23HALF_SQUARE_HEIGHT = 2.5
24SQUARE_SPACING = 10
25
26
27class GameView(arcade.View):
28 """ Main application class. """
29
30 def __init__(self):
31 super().__init__()
32
33 self.background_color = arcade.color.DARK_SLATE_GRAY
34
35 self.draw_time = 0
36 self.shape_list = None
37
38 def setup(self):
39 self.shape_list = arcade.shape_list.ShapeElementList()
40
41 # --- Create all the rectangles
42
43 # We need a list of all the points and colors
44 point_list = []
45 color_list = []
46
47 # Now calculate all the points
48 for x in range(0, WINDOW_WIDTH, SQUARE_SPACING):
49 for y in range(0, WINDOW_HEIGHT, SQUARE_SPACING):
50
51 # Calculate where the four points of the rectangle will be if
52 # x and y are the center
53 top_left = (x - HALF_SQUARE_WIDTH, y + HALF_SQUARE_HEIGHT)
54 top_right = (x + HALF_SQUARE_WIDTH, y + HALF_SQUARE_HEIGHT)
55 bottom_right = (x + HALF_SQUARE_WIDTH, y - HALF_SQUARE_HEIGHT)
56 bottom_left = (x - HALF_SQUARE_WIDTH, y - HALF_SQUARE_HEIGHT)
57
58 # Add the points to the points list.
59 # ORDER MATTERS!
60 # Rotate around the rectangle, don't append points caty-corner
61 point_list.append(top_left)
62 point_list.append(top_right)
63 point_list.append(bottom_right)
64 point_list.append(bottom_left)
65
66 # Add a color for each point. Can be different colors if you want
67 # gradients.
68 for i in range(4):
69 color_list.append(arcade.color.DARK_BLUE)
70
71 shape = arcade.shape_list.create_rectangles_filled_with_colors(point_list, color_list)
72 self.shape_list.append(shape)
73
74 def on_draw(self):
75 """
76 Render the screen.
77 """
78
79 # This command has to happen before we start drawing
80 self.clear()
81
82 # Start timing how long this takes
83 draw_start_time = timeit.default_timer()
84
85 # --- Draw all the rectangles
86 self.shape_list.draw()
87
88 output = f"Drawing time: {self.draw_time:.3f} seconds per frame."
89 arcade.draw_text(output, 20, WINDOW_HEIGHT - 40, arcade.color.WHITE, 18)
90
91 self.draw_time = timeit.default_timer() - draw_start_time
92
93
94def main():
95 """ Main function """
96 # Create a window class. This is what actually shows up on screen
97 window = arcade.Window(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE)
98
99 # Create and setup the GameView
100 game = GameView()
101 game.setup()
102
103 # Show GameView on screen
104 window.show_view(game)
105
106 # Start the arcade game loop
107 arcade.run()
108
109
110if __name__ == "__main__":
111 main()