Arcade is using OpenGL for the underlying rendering. OpenGL functionality is given to use through pyglet when a window is crated. The underlying representation of this is an OpenGL context. Arcade’s representation of this context is the arcade.Window.ctx. This is an ArcadeContext.

Working with OpenGL adds some challenges we need to be aware of.


Certain operations can’t be done before a window is created. In Arcade we do deferred initialization in many of our types to make this as painless as possible for the user. SpriteList can for example be built before window creation and will be initialized internally in the first draw call.

TextureAtlas on the other hand cannot be crated before the window is created, but Texture can freely be loaded at any time since these only manage pixel data with Pillow and calculate hit box data on the cpu.

Garbage Collection & Threads#

OpenGL is not thread safe meaning doing actions from anything but the main thread is not possible. You can still use threads with arcade, but they cannot interact with anything that affects OpenGL objects. This will throw an error immediately.

When threads are used in a project or underlying libraries there is always the risk that Python’s garbage collector will run outside the main thread. This is just how Python’s garbage collector works.

For this reason, Arcade’s default garbage collection mode requires actively releasing OpenGL objects. We are doing this for you in the arcade.Window.flip() method that is automatically called every frame.

This garbage collection mode is called context_gc since dead OpenGL objects are collected in the context and only released when ctx.gc() is called.

Garbage collection modes can be configured during window creation or changed runtime in the context.

# auto mode works like python's garbage collection (but more risky)
window = Window(gc_mode="auto")

# This context mode is implied by default
window = Window(gc_mode="context_gc")
# From now on you need to manually call window.ctx.gc()
# for OpenGL resources to be deleted. This can be
# done very frame if needed or in shorter intervals
num_released = window.ctx.gc()
print("Resources released:", num_released)

# Change gc mode runtime
window.gc_mode = "auto"
window.gc_mode = "context_gc"

If you for some reason need garbage collection to run more often than once per frame it can safely be called as many times as you want from the main thread.

In the vast majority of cases this is nothing you need to be worried about. The current default exists to make your life as easy as possible.

Threads & vsync#

Note that if vsync is enabled all threads will stall when all rendering is done and OpenGL is waiting for the next vertical blank. The only way to combat this is to disable vsync or use sub-processes.

SpriteList & Threads#

SpriteLists can be created in threads if they are created with the lazy=True parameters. This ensures OpenGL resources are not created until the first draw() call or initialize() is called.

Writing Raw Bytes to GL Buffers & Textures#

Many of arcade’s OpenGL classes support creation from or writing to any object that supports the buffer protocol. The classes most useful to end users are:

This functionality can be used for displaying the results of calculations such as:

  • Scientific visualizations displaying data from numpy arrays

  • Simple console emulators drawing their internal screen buffer

There should be no typing issues when using Python’s built-in buffer protocol objects as arguments to the write method of arcade’s GL objects. We list these built-in types in the arcade.arcade_types.BufferProtocol Union type.

For objects from third-party libraries, your type checker may warn you about type mismatches. This is because Python will not support general annotations for buffer protocol objects until version 3.12 at the earliest.

In the meantime, there are workarounds for users who want to write to arcade’s GL objects from third-party buffer protocol objects:

  • use the typing.cast method to convert the object’s type for the linter

  • use # type: ignore to silence the warnings