One canvas API, multiple backends
See the codeOne canvas API, multiple backends 🚀
This project is part of pygfx.org
See how the two windows above look the same? That's the idea; they also look the same to the code that renders to them. Yet, the GUI systems are very different (Qt vs glfw in this case). Now that's a powerful abstraction!
Providing a generic API for:
BaseRenderCanvas).wgpu (WgpuContext).BitmapContext).The following backends are currently available:
auto - automatically selects an appropriate backend.glfw - a native desktop window.offscreen - for offscreen rendering.terminal - to render in the terminal (e.g. over SSH).qt (pyside6 / pyside2 / pyqt6 / pyqt5) - to embed a render canvas in a qt GUI.wx - to embed a render canvas in a wx GUI.anywidget - for notebooks like Jupyter, VSCode, and Marimo.jupyter - previous backend for notebooks that uses jupyter_rfb.http - for server side rendering, display in a browser connected via the internet.pyodide - to run in the browser with Pyodide or PyScript.In addition to the GUI libraries mentioned above, the following event loops are supported:
asynciotriorawpip install rendercanvas
To have at least one backend, we recommend:
pip install rendercanvas glfw
Also see the online documentation and the examples.
A minimal example that renders noise:
import numpy as np
from rendercanvas.auto import RenderCanvas, loop
canvas = RenderCanvas(update_mode="continuous")
context = canvas.get_bitmap_context()
@canvas.request_draw
def animate():
w, h = canvas.get_logical_size()
bitmap = np.random.uniform(0, 255, (h, w)).astype(np.uint8)
context.set_bitmap(bitmap)
loop.run()
Run wgpu visualizations:
from rendercanvas.auto import RenderCanvas, loop
from rendercanvas.utils.cube import setup_drawing_sync
canvas = RenderCanvas(
title="The wgpu cube example on $backend", update_mode="continuous"
)
draw_frame = setup_drawing_sync(canvas)
canvas.request_draw(draw_frame)
loop.run()
Embed in a Qt application:
from PySide6 import QtWidgets
from rendercanvas.qt import QRenderWidget
class Main(QtWidgets.QWidget):
def __init__(self):
super().__init__()
splitter = QtWidgets.QSplitter()
self.canvas = QRenderWidget(splitter)
...
app = QtWidgets.QApplication([])
main = Main()
app.exec()
We support both; a render canvas can be used in a fully async setting using e.g. Asyncio or Trio, or in an event-driven framework like Qt.
If you like callbacks, loop.call_later() always works. If you like async, use loop.add_task().
See the docs on async for details.
This code is distributed under the 2-clause BSD license.
See the contribution guide.
rendercanvas and developer deps using pip install -e .[dev].ruff format to apply autoformatting.ruff check to check for linting errors.pytest tests to run the tests.pytest examples to run a subset of the examples.pytest tests/test_backend_xx.py to run backend-specific tests, e.g. for glfw, qt, wx.This repository follows the PyGfx Code of Conduct
1,565 followers · starred Mar 2025
15 followers · starred Jul 2026
5 followers · starred Mar 2025
One canvas API, multiple backends
See the codeOne canvas API, multiple backends 🚀
This project is part of pygfx.org
See how the two windows above look the same? That's the idea; they also look the same to the code that renders to them. Yet, the GUI systems are very different (Qt vs glfw in this case). Now that's a powerful abstraction!
Providing a generic API for:
BaseRenderCanvas).wgpu (WgpuContext).BitmapContext).The following backends are currently available:
auto - automatically selects an appropriate backend.glfw - a native desktop window.offscreen - for offscreen rendering.terminal - to render in the terminal (e.g. over SSH).qt (pyside6 / pyside2 / pyqt6 / pyqt5) - to embed a render canvas in a qt GUI.wx - to embed a render canvas in a wx GUI.anywidget - for notebooks like Jupyter, VSCode, and Marimo.jupyter - previous backend for notebooks that uses jupyter_rfb.http - for server side rendering, display in a browser connected via the internet.pyodide - to run in the browser with Pyodide or PyScript.In addition to the GUI libraries mentioned above, the following event loops are supported:
asynciotriorawpip install rendercanvas
To have at least one backend, we recommend:
pip install rendercanvas glfw
Also see the online documentation and the examples.
A minimal example that renders noise:
import numpy as np
from rendercanvas.auto import RenderCanvas, loop
canvas = RenderCanvas(update_mode="continuous")
context = canvas.get_bitmap_context()
@canvas.request_draw
def animate():
w, h = canvas.get_logical_size()
bitmap = np.random.uniform(0, 255, (h, w)).astype(np.uint8)
context.set_bitmap(bitmap)
loop.run()
Run wgpu visualizations:
from rendercanvas.auto import RenderCanvas, loop
from rendercanvas.utils.cube import setup_drawing_sync
canvas = RenderCanvas(
title="The wgpu cube example on $backend", update_mode="continuous"
)
draw_frame = setup_drawing_sync(canvas)
canvas.request_draw(draw_frame)
loop.run()
Embed in a Qt application:
from PySide6 import QtWidgets
from rendercanvas.qt import QRenderWidget
class Main(QtWidgets.QWidget):
def __init__(self):
super().__init__()
splitter = QtWidgets.QSplitter()
self.canvas = QRenderWidget(splitter)
...
app = QtWidgets.QApplication([])
main = Main()
app.exec()
We support both; a render canvas can be used in a fully async setting using e.g. Asyncio or Trio, or in an event-driven framework like Qt.
If you like callbacks, loop.call_later() always works. If you like async, use loop.add_task().
See the docs on async for details.
This code is distributed under the 2-clause BSD license.
See the contribution guide.
rendercanvas and developer deps using pip install -e .[dev].ruff format to apply autoformatting.ruff check to check for linting errors.pytest tests to run the tests.pytest examples to run a subset of the examples.pytest tests/test_backend_xx.py to run backend-specific tests, e.g. for glfw, qt, wx.This repository follows the PyGfx Code of Conduct
1,565 followers · starred Mar 2025
15 followers · starred Jul 2026
5 followers · starred Mar 2025