Skip to content

displaydev: one-call canvas blits, and WorkerDisplay for Python in a Web Worker - #207

Merged
bdbarnett merged 4 commits into
mainfrom
ostinato/display
Oct 15, 2026
Merged

bdbarnett merged 4 commits into
mainfrom
ostinato/display

Conversation

@bdbarnett

Copy link
Copy Markdown
Contributor

Faster PyScript drawing

PSDisplay.blit_rect converted RGB565 to RGBA one pixel at a time through the Python-to-JavaScript bridge, four calls a pixel. A full 1280x720 LVGL frame took about two seconds. It now hands the whole buffer to JavaScript once and converts it there: about 60 ms for the same frame in Chrome, with byte-for-byte the same pixels (checked against the old lookup table on random data).

Python in a Web Worker

A long redraw on the page's main thread holds up everything else the page has to do on time. displaydev.workerdisplay.WorkerDisplay lets your Python run in a Web Worker instead: the page transfers its canvas as an OffscreenCanvas and relays pointer, wheel and key events, and get_events hands them out exactly as PSDisplay does, so appdev and LVGL's display_driver work unchanged. board_configs/workerdisplay pairs them. The module docstring has the page-side JavaScript and the event format.

To share code between the two, the canvas drawing moves into displaydev/_webcanvas.py and the input handling into displaydev/_webinput.py (neither touches document, so both import in a worker). PSDisplay's public behaviour is unchanged. Both new helpers are listed in pydevices-desktop.toml and mip-split.toml.

Tests

tests/test_workerdisplay.py: one JavaScript call per blit, bounds checks, mouse events in CSS pixels, touch mapped to framebuffer pixels, keys with auto-repeat dropped, wheel. The existing display tests pass.

…ay for an OffscreenCanvas in a Web Worker

PSDisplay.blit_rect converted RGB565 one pixel at a time through the
Python-to-JavaScript bridge, four calls a pixel: about two seconds for a full
1280x720 LVGL frame. It now converts the whole buffer in one JavaScript call,
byte for byte the same pixels.

The drawing and the input handling move into two shared helpers
(_webcanvas, _webinput) so a second canvas display can use them:
WorkerDisplay draws on an OffscreenCanvas the page transfers to a Web Worker
and takes its input from events the page relays. A board config,
board_configs/workerdisplay, pairs them, and LVGL's display_driver runs on it
unchanged.
Without a pyscript module, a Pyodide Web Worker fell through to wake sources
that never fire there, so timers (LVGL's display_driver loop among them)
armed and never ran.
@bdbarnett
bdbarnett merged commit 1b1c4ea into main Oct 15, 2026
3 checks passed
@bdbarnett
bdbarnett deleted the ostinato/display branch October 15, 2026 07:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant