The fastest 2D object rendering library for Python
Project description
FastObjects
The fastest 2D object rendering library for Python.
Documentation · Documentação em português
Sprites sustained at 60 fps in the bunnymark arena, measured on the same machine (AMD Radeon RX 580, Python 3.13, 2026-07-07) against other Python rendering libraries:
| Framework | Sprites @ 60 fps |
|---|---|
| fastobjects | 328,213 |
| arcade | 3,795 |
| raylib | 3,795 |
| pygame-ce | 2,530 |
| pyglet | 2,530 |
That is 86x the closest competitor. Numbers vary between runs by one ramp
step (±1.5x); see benchmarks/RESULTS.md for the
full dated series, methodology, and hardware details before quoting a number
as "current".
Installation
pip install fastobjects
Requires Python ≥ 3.11 and OpenGL 3.3 core.
Quick start
import fastobjects as fo
win = fo.Window(800, 600, title="FastObjects demo")
sprites = fo.SpriteBatch("player.png", capacity=1000) # any image file
group = sprites.spawn(1, x=400, y=300)
@win.frame
def update(dt: float) -> None:
if win.keys[fo.KEY_RIGHT]:
group.pos[:, 0] += 200 * dt
win.clear(0.1, 0.1, 0.1)
win.draw(sprites)
if win.keys[fo.KEY_ESCAPE]:
win.request_close()
win.run()
Windowopens a native GLFW window with an OpenGL 3.3 core context and drives the frame loop (run()calls your@win.framecallback every frame withdt).win.keys[fo.KEY_X]andwin.mouseexpose polled input state.SpriteBatchholds up tocapacitytextured sprites;spawn()returns aSpriteGroupwhose.pos,.size,.rot, and.colorare NumPy views into the batch — writing to them updates the sprites directly, no per-object overhead.batch.despawn(group)removes a group and frees its capacity; the other groups' handles stay valid.ShapeBatchworks the same way for rectangles, circles, and lines (batch.rects(...),batch.circles(...),batch.lines(...)), useful for debug overlays or non-textured geometry.win.draw(*batches)issues one draw call per batch, in the order given.
Why it's fast
Three decisions, each validated by benchmark (every experiment — winners and
losers — is recorded in benchmarks/RESULTS.md):
- No Python objects per sprite. State lives in flat NumPy columns (structure-of-arrays); updates are vectorized array math, never a per-object loop.
- One instanced draw call per batch. The quad is generated in the vertex shader; per-instance attributes stream from one VBO per column.
- You pay for change, not existence. Positions upload every frame; size, rotation, and color upload only in the frames you touch them (automatic, conservative dirty tracking). A typical frame uploads 8 bytes per sprite instead of 40.
The result: at 100,000 moving objects, FastObjects sustains 384 fps —
above the 353 fps of a minimal hand-written moderngl renderer used as the
technique's reference ceiling (which pays extra CPU copies per frame that
FastObjects avoids).
Use it inside pygame
pygame owns the window, events, input, and sound; FastObjects owns object
insertion, update, removal, and rendering. Classic pygame drawing
(pygame.draw, pygame.font) composites on top via SurfaceLayer:
import pygame
import fastobjects as fo
pygame.init()
pygame.display.set_mode((1280, 720), pygame.OPENGL | pygame.DOUBLEBUF)
ext = fo.attach(view_size=(1280, 720))
batch = fo.SpriteBatch("player.png", capacity=10_000)
group = batch.spawn(1000, x=640, y=360)
hud_surface = pygame.Surface((1280, 720), pygame.SRCALPHA)
hud = fo.SurfaceLayer(hud_surface)
while True:
for event in pygame.event.get():
...
ext.clear(0.1, 0.1, 0.1)
batch.draw()
hud.update()
hud.draw()
pygame.display.flip()
See examples/pygame_interop.py for the
complete, runnable version (click to spawn, D to despawn, pygame-font HUD).
Examples
examples/bunnymark.py— 100k bouncing bunnies, native window, FPS counter.examples/shapes_input.py— shapes + polled keyboard/mouse input.examples/pygame_interop.py— FastObjects rendering inside a pygame window.
Development
Install with development dependencies:
pip install -e ".[dev,bench]"
Run the test suite (98 tests, pixel-verified against an offscreen OpenGL
context) with pytest, and the benchmark arena with
python benchmarks/arena/run_all.py.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fastobjects-0.3.1.tar.gz.
File metadata
- Download URL: fastobjects-0.3.1.tar.gz
- Upload date:
- Size: 128.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d2a114d3d3226e7af82162f1deff3c0a32e6beec6168b107d07468ab1c400fd1
|
|
| MD5 |
e75761ef989f688d62b114aa79cb5cf5
|
|
| BLAKE2b-256 |
793a11cdbd2a86a85af5c205ae353c4fac4d100df0e65a20c4fda5231ce62929
|
Provenance
The following attestation bundles were made for fastobjects-0.3.1.tar.gz:
Publisher:
publish.yml on Enzo-Azevedo/FastObjects
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastobjects-0.3.1.tar.gz -
Subject digest:
d2a114d3d3226e7af82162f1deff3c0a32e6beec6168b107d07468ab1c400fd1 - Sigstore transparency entry: 2119656863
- Sigstore integration time:
-
Permalink:
Enzo-Azevedo/FastObjects@4d74a9a58eb096bdbbe47e740669a036c7a56d4c -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/Enzo-Azevedo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4d74a9a58eb096bdbbe47e740669a036c7a56d4c -
Trigger Event:
push
-
Statement type:
File details
Details for the file fastobjects-0.3.1-py3-none-any.whl.
File metadata
- Download URL: fastobjects-0.3.1-py3-none-any.whl
- Upload date:
- Size: 20.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a8f8bc1b9fa562068c27e52003577f0aea71259cc47d35be370183bec8685f3b
|
|
| MD5 |
1a7970b667ca716713caa3a9b781afdb
|
|
| BLAKE2b-256 |
3b4dec04cc35fca2c8fac1ed460574bcbed66bf68b33ce73f0dba017c3ca4d5c
|
Provenance
The following attestation bundles were made for fastobjects-0.3.1-py3-none-any.whl:
Publisher:
publish.yml on Enzo-Azevedo/FastObjects
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastobjects-0.3.1-py3-none-any.whl -
Subject digest:
a8f8bc1b9fa562068c27e52003577f0aea71259cc47d35be370183bec8685f3b - Sigstore transparency entry: 2119656949
- Sigstore integration time:
-
Permalink:
Enzo-Azevedo/FastObjects@4d74a9a58eb096bdbbe47e740669a036c7a56d4c -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/Enzo-Azevedo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4d74a9a58eb096bdbbe47e740669a036c7a56d4c -
Trigger Event:
push
-
Statement type: