glint turns a supported ESP32 board with an LCD into a small extra display for a computer.
I built it because the usual 3.5" ESP32 panels are sharp enough for status work, but most "USB display" projects either depend on proprietary stacks or stop at a one-off demo. glint treats the panel as a real endpoint: firmware on the board, a macOS host that creates a virtual display, and a Linux host that can drive the same panel from a framebuffer.
Demo video: coming soon.
I will add a recorded walkthrough here showing the macOS virtual display path, the Linux framebuffer path, and the supported boards in use.
- macOS host: creates a real virtual display through private CoreGraphics API, then streams that display to the panel over USB or Wi-Fi.
- Linux host: mirrors a framebuffer to the same panel with a pure-Go host that uses usbfs directly and needs no X server.
- Firmware: receives tiled pixel updates over a USB vendor interface or TCP on Wi-Fi, paints them on the panel, and reports touch events back.
At this size and density it works best as a status panel: a docked terminal, a dashboard, a timer, a monitor widget. Text is good. Gradients and photographs are not the point.
I wanted a small display that behaves like part of the machine rather than a screen driven by a custom app. On macOS that meant creating a real display and streaming it. On Linux that meant keeping the wire protocol host-agnostic so a Pi or desktop could drive the same panel without any virtual-display machinery.
| Platform | Status | Notes |
|---|---|---|
| macOS | supported | Swift host, virtual display, USB and Wi-Fi, touch support |
| Linux | supported | pure-Go host, usbfs transport, framebuffer mirroring |
| Windows | partial | Go host drives the panel over Wi-Fi (measured 27.7 fps); USB needs a WinUSB backend |
I have tested glint on these boards:
| Board | Panel | Link |
|---|---|---|
| ESP32-P4-WIFI6-Touch-LCD-3.5 | ST7796 SPI, 320x480, FT6336 touch | USB high speed, Wi-Fi |
| Waveshare ESP32-S3-Touch-AMOLED-1.75 | CO5300 QSPI, 466x466, CST9217 touch | USB full speed, Wi-Fi |
Nothing in the host code is specific to those two boards. A board is one header
in firmware/main/boards/ plus any panel- or touch-driver support it needs in
firmware. The panel size and capabilities come from the handshake, so both
hosts stay resolution-agnostic.
Hardware details, flashing notes, board bring-up, and touch mappings live in HARDWARE.md.
- Install ESP-IDF v5.5 at
~/esp/esp-idf-v5.5, or setIDF_PATH. - Connect a supported board.
- Run:
make setupmake setup finds the board, suggests the matching profile, offers Wi-Fi
configuration, builds, flashes, and starts a display session.
If you prefer to do it by hand:
make # build firmware + macOS host
make flash # flash the board
make display # macOS extended desktop on the panelWhole frames of colour bars, no dirty-rect tiling, frame cap lifted — so these are link ceilings rather than what a desktop actually costs.
| ESP32-P4 (USB high speed) | ESP32-S3 AMOLED (USB full speed) | |
|---|---|---|
| USB | 7.4-7.9 MB/s, 24-26 fps at 320x480 | 0.46 MB/s, about 1 fps at 466x466 |
| Wi-Fi | 0.95-1.9 MB/s, 3-6 fps | not measured |
| Bound by, on USB | the ST7796's SPI bus, about 8 MB/s | the 1.2 MB/s full-speed USB ceiling |
| Bound by, on Wi-Fi | the radio and its round-trip time | — |
Two things worth knowing about those ranges. On USB neither board is limited by the host: profiling puts 94-100% of each frame in the USB write, with render and encode together under 3 ms. On Wi-Fi the figure halves run to run as the 2.4 GHz band gets busy, and the P4 has no radio of its own — its Wi-Fi is an ESP32-C6 reached over SDIO, which adds a hop.
A desktop is far cheaper than a full frame, because most of it does not change between frames:
| Full frame, uncompressed | 300 KB |
| Dirty-tile push, typical desktop | 18 KB, 2.4 tiles per frame |
| Idle desktop | most frames send nothing |
| Flat 64x64 tile, RLE | 8192 bytes to 4 |
Per-platform figures, including a Raspberry Pi driving the same panel, are in the macOS and Linux guides.
- macOS cannot create a virtual display at the panel's native size, so the default desktop is downscaled 2:1. See NOTES.md.
CGVirtualDisplayis private API and can break on a macOS update.- HiDPI is ignored for virtual displays.
- The P4 board's backlight is a plain GPIO, so
CMD_BACKLIGHTis effectively on/off there. - The Linux USB path is usbfs-based and Linux-only.
- Screen Recording permission is required on macOS, and the recording indicator stays on for as long as a session runs.
- On Windows the firmware binds WinUSB, but the Go host has no USB backend for it yet, so Windows drives the panel over Wi-Fi.
make test runs:
- Swift unit tests for the shared host logic
- host-compiled C tests for the firmware RLE decoder
go test ./...across the Linux host packages
The Swift, Go, and firmware-side RLE implementations are pinned to the same fixture so they agree byte-for-byte.
- HARDWARE.md: boards, pins, flashing, touch mappings
- DESIGN.md: architecture and design decisions
- PROTOCOL.md: wire format
- NOTES.md: platform constraints and non-obvious behavior
- THIRD-PARTY.md: third-party code and licenses
protocol/protocol.h wire format source of truth
firmware/ ESP-IDF firmware
components/ third-party code, under its own licence
host/Sources/GlintCore/ shared host logic: protocol, tiling, RLE
host/Sources/glint/ macOS host CLI and virtual-display path
linux/ Linux host and transport code
packaging/ LaunchAgent and udev rule
tools/ setup helpers
Releases carry prebuilt hosts and firmware, so trying glint needs no toolchain:
glint.exe,glint-pi-arm64,glint-pi-armv6,glint-linux-amd64— single binaries with no installer and no kernel driver. They drive a panel over Wi-Fi, and over USB on Linux, where the USB path needs the one udev rule inpackaging/so a normal user can open the device.glint-firmware-p4.zip,glint-firmware-amoled.zip— the images plus the exactesptoolcommand. Wi-Fi is off in released firmware; enable it from a source checkout.
The macOS host builds from source, because it links Homebrew's libusb:
brew tap shubham030/glint
brew install glintBuilding the firmware, or changing board configuration, still wants a clone and ESP-IDF — see the macOS guide.
MIT — see LICENSE. Third-party code keeps its own terms, listed in THIRD-PARTY.md.