Cross-platform display, input-event, and timer drivers for MicroPython, CircuitPython, and CPython.
Documentation • Browser demo • Wokwi simulator • Screenshot gallery
![]() |
![]() |
|---|---|
@peterhinch's active.py |
@russhughes's tiny_toasters.py |
PyDisplay is the portable foundation layer for Python graphics. It gives you display drivers, unified input events, drawing primitives, fonts, palettes, and cross-platform timers behind a single API — so the same drawing code runs unchanged on a $4 microcontroller, on your desktop, in a web browser, and even inside a Jupyter Notebook.
- MicroPython — on microcontrollers, on Unix, on Windows, in the browser via PyScript, and in the Wokwi online simulator.
- CircuitPython — on microcontrollers and on Unix.
- CPython — on Unix, on Windows, and in Jupyter Notebook.
Develop and debug on your laptop with a mouse, then deploy the same code to a touchscreen on an ESP32 — no display rewrite, no input rewrite, no timer rewrite.
PyDisplay is the graphics, input, and timing backend for a growing family of projects. The sister projects lv_micropython_cmod, lv_circuitpython_mod, and lv_cpython_mod wire PyDisplay into LVGL for MicroPython, CircuitPython, and CPython respectively. That means you can build a polished LVGL application in pure Python — and you can even develop your LVGL apps interactively in a Jupyter Notebook, then run the identical code on a microcontroller.
It also drops straight in under Nano-GUI, MicroPython-Touch, russhughes-style TFT/st7789py apps, the bundled PyWidgets toolkit, or your own widget library.
- One API, every platform —
framebuf-compatible drawing on MicroPython, CircuitPython, and CPython. - Unified input — touch, mouse, keyboard, keypad, rotary encoder, and joystick all arrive as the same PyGame/SDL2-style events.
- Cross-platform timers — the
multimerpackage gives youmachine.Timer-style andasynciotimers on hosts that have neither. - Batteries included — 30 ready-made board configs, 60+ example scripts, a browser demo, and a Wokwi project.
- Flexible install — full git clone, one-line MIP packages, or precompiled
.mpybytecode.
Alpha quality. APIs and docs are still evolving fast. Feedback and contributions are very welcome.
- Introduction 1.1 What PyDisplay is 1.2 What PyDisplay is not
- Portability — the single most important feature: where PyDisplay runs.
- Quick start 3.1 Try it in the browser — zero install. 3.2 Desktop (CPython) 3.3 MicroPython board
- Installation — clone, MIP, or precompiled packages.
- Architecture at a glance
- Ecosystem & sister projects — LVGL in Python, GUIs, Jupyter.
- Documentation map — where everything lives.
- Contributing
- Credits & license
PyDisplay is a foundation layer, not a GUI toolkit. It provides:
displaysys— display backends (BusDisplay,SDLDisplay,PGDisplay,PSDisplay,JNDisplay,FBDisplay) with a unified drawing API.eventsys— a broker that turns touch, mouse, keyboards, keypads, encoders, and joysticks into uniform PyGame/SDL2-style events.graphics— a portableframebuf-compatible drawing surface plus shapes, fonts, bitmap loaders, andAreahelpers.multimer— cross-platform periodic timers (sync, threaded, polled, andasyncio) with amachine.Timer-style API.
Use it directly for simple UIs, or as the backend for a full widget library.
- Not a widget toolkit — no built-in buttons, sliders, or layout managers (use LVGL, Nano-GUI, or the bundled PyWidgets add-on).
- Not a task scheduler — use
multimerorasynciofor timing.
See the Architecture guide for the full mental model.
Portability is PyDisplay's headline feature. The same application code runs across every supported runtime and target:
| Runtime | Microcontrollers | Unix / Linux | Windows | Browser | Jupyter Notebook |
|---|---|---|---|---|---|
| MicroPython | ✅ | ✅ | ✅ | ✅ PyScript · Wokwi | — |
| CircuitPython | ✅ | ✅ | — | — | — |
| CPython | — | ✅ | ✅ | — | ✅ |
The right display backend is selected automatically: BusDisplay on MCUs, SDLDisplay or PGDisplay on the desktop, PSDisplay in PyScript, JNDisplay in Jupyter, and FBDisplay for CircuitPython framebuffer displays. Your code just imports board_config and draws.
Full details and per-platform notes: Portability & platforms.
Open the live PyScript demo or the Wokwi simulator — both run real PyDisplay code in your browser with nothing to install.
git clone https://github.com/PyDevices/pydisplay.git
cd pydisplay/src
python3 -i path.py>>> import pydisplay_demoA display window opens (PyGame or SDL2, whichever is installed). See the Desktop CPython guide.
import mip
mip.install("github:PyDevices/pydisplay/installer.py")
import installer # runs the default installThen pick a board config for your hardware and follow the ESP32 board guide.
| Channel | Format | Best for |
|---|---|---|
| Full clone | Entire repo | Development, desktop, contributing |
installer.py |
Source files | One-shot setup on a MicroPython board |
| GitHub MIP | Source .py |
Picking individual packages |
| micropython-lib MIP | Precompiled .mpy |
Smallest footprint on device |
Precompiled packages live in the PyDevices micropython-lib MIP index. See the installation overview to choose.
board_config.py (selects + wires your hardware / platform)
│
├── displaysys ──► display_drv (draw: fill_rect, blit_rect, show, …)
├── eventsys ──► broker (poll: touch / mouse / keys / encoder)
├── graphics (shapes, fonts, framebuf, Area)
└── multimer (periodic + asyncio timers)
│
▼
your app · LVGL · Nano-GUI · MicroPython-Touch · PyWidgets
from board_config import display_drv, broker
while True:
for event in broker.poll():
... # handle touch, clicks, keys, encoder
display_drv.fill_rect(0, 0, 10, 10, 0xF800)
display_drv.show()Full diagram and boot sequence: Architecture.
PyDisplay is the graphics, input-event, and timing backend for the wider PyDevices ecosystem:
| Project | What it adds |
|---|---|
| lv_micropython_cmod | LVGL bindings for MicroPython, using PyDisplay for display and input. |
| lv_circuitpython_mod | LVGL bindings for CircuitPython, backed by PyDisplay. |
| lv_cpython_mod | LVGL bindings for CPython, backed by PyDisplay. |
Because all three share PyDisplay as the backend, you can build an LVGL app in pure Python and develop it interactively in a Jupyter Notebook, then run the identical code on a microcontroller. See the LVGL guide and the Jupyter walkthrough.
PyDisplay also integrates with:
- Nano-GUI and MicroPython-Touch by @peterhinch
- russhughes TFT / st7789py ports
- the bundled PyWidgets toolkit (
add_ons/pdwidgets)
Everything lives in one place — the documentation site:
| Topic | Start here |
|---|---|
| First steps | Getting started |
| Where it runs | Portability & platforms |
| Core concepts | Architecture, Displays, Events, multimer |
| Hardware | Board configs |
| Examples | Examples catalog |
| GUI libraries | GUI integration |
| API | API reference |
Fork, branch, and open a PR. See the Contributing guide and the open GitHub Issues. Roadmap items include EPaper displays, CircuitPython circup packages, end-user PyPI wheels, and more C bus drivers.
PyDisplay is released under the MIT License. Copyright © 2024 Brad Barnett.
Thanks to @peterhinch, @russhughes, and the Adafruit CircuitPython team for foundational work in the Python-on-microcontrollers community.

