# FAQ (/faq)
## Do I need an account or a cloud? [#do-i-need-an-account-or-a-cloud]
No. A [standalone frame](/guide/standalone) needs nothing but an SD card, and the
[self-hosted backend](/guide/backend) is a web app **you** host. Frames run entirely on-device
once deployed: shut the backend down and they keep rendering, keep their schedules, and stay
controllable over their [local HTTP API](/guide/control).
There *is* a cloud if you want one: [FrameOS Cloud](/guide/cloud) manages frames from a browser
with nothing to host, and is free while in beta. It is deliberately limited - no shell on your
frame, no compiled code, only sandboxed interpreted scenes - and a cloud-managed frame keeps
working when the cloud is unreachable. Self-hosting is not going anywhere - in fact, the entire
cloud is AGPL too, and you can self-host it as well.
## Can I switch between standalone, self-hosted and cloud? [#can-i-switch-between-standalone-self-hosted-and-cloud]
Yes, without reflashing. A frame is managed by one of them at a time: **Adopt frame** in the
backend takes over a standalone frame, the link code on a frame's display claims it for the cloud,
and **Disconnect** on the frame's admin page (or deleting it in the cloud) makes it standalone
again, scenes intact. See [Moving between cloud, backend and standalone](/guide/cloud#moving-between-cloud-backend-and-standalone).
## Can I run this on an ESP32? [#why-no-microcontrollers]
Yes, and it's a full frame rather than a demo. FrameOS runs on the **ESP32-S3**: you
[flash the signed release firmware from the browser](/guide/esp32) - nothing to build - and the device renders
scenes itself - the same interpreter as Linux frames, the same apps, JavaScript on QuickJS, its
own scheduler, assets on an SD card, logs and metrics back to the backend, and firmware updates
over the air with automatic rollback. It deep-sleeps between refreshes and wakes on a button
press, so battery-powered frames are real.
The short list of what a microcontroller frame *can't* do: Waveshare-family SPI e-paper only (no
HDMI, no LCD, no Pimoroni HATs), no custom Nim apps (JavaScript instead), one built-in font plus
TTFs off the SD card but no emoji fallback, and no headless Chromium screenshots or RTSP camera
snapshots. The full picture is on the [ESP32 page](/guide/esp32#what-it-cannot-do).
The **Raspberry Pi is still the broadest platform** - [120+ panels](/devices), HDMI at 60 FPS,
custom Nim, any font you upload. Pick the Pi if you want everything; pick the ESP32 if you want a
wire-free frame, or if you already own one of the
[all-in-one boards](/guide/esp32#supported-boards) like a Waveshare PhotoPainter, a TRMNL or a
Seeed reTerminal.
## Can it really render a huge photo on a microcontroller? [#big-images]
Yes. Point a frame at a 4000×3000 photo and it shows it, on a board with 8 MB of RAM. The
decoder scales *while* it decodes, straight into the panel-sized canvas, so the full-size image
never exists; downloads too big to hold at all spool to the SD card and stream from there. No
proxy, no resize server, no `?width=` parameters. See
[Big images on small devices](/guide/images).
## Can I run a frame with no hardware? [#virtual]
Yes - a [virtual frame](/guide/virtual) is rendered by the backend and served as an image URL and
a self-refreshing kiosk page. Point an old tablet, a browser or a signage player at it and you
have a frame with nothing installed on the device. It's also the fastest way to try FrameOS while
you wait for a panel to arrive.
## Can the backend and the frame run on the same Raspberry Pi? [#can-the-backend-and-the-frame-run-on-the-same-raspberry-pi]
Yes, on a Pi 4/5 with some patience. Install the backend via the
[release tarballs or Docker](/guide/backend) on the Pi, then add the frame with `localhost` as its
address. For e-ink frames a more common setup is: run the backend on your laptop only when you
want to change something - the frame doesn't need it day to day.
## What happens when my WiFi or internet goes down? [#what-happens-when-my-wifi-or-internet-goes-down]
The frame keeps rendering whatever its scenes can produce locally. Apps that fetch remote data
serve their cached values. The control page on port 8787 keeps working as long as you can reach
the frame on the network. When connectivity returns, everything catches up on the next render.
## How fast can it render? [#how-fast-can-it-render]
On a Raspberry Pi 5 over HDMI: up to **60 frames per second**. On e-ink, the panel itself is the
bottleneck - color e-ink takes \~15-30 seconds per refresh, monochrome panels a second or two.
FrameOS only refreshes the physical display when the rendered image actually changed, so e-ink
panels don't flash needlessly.
## Can I show my own photos? [#can-i-show-my-own-photos]
Yes, several ways. Upload images to the frame's **Assets** (or drop them on its SD card) and use
the *Local image* app - point it at a folder and it plays the folder. Pull from
**Immich** or **Google Photos**. Point the *Image from URL* app at any address. Or use the gallery
apps: FrameOS Gallery, Unsplash, Wikimedia Commons, OpenAI image generation.
You don't need to resize anything first - see [Big images on small devices](/guide/images).
## Can I drive it from a script or an AI agent? [#can-i-drive-it-from-a-script-or-an-ai-agent]
Yes, at two levels. Every frame has a [local HTTP API](/guide/control) on port 8787 - change
state, switch scenes, trigger renders with `curl`, no account needed. And a
[FrameOS Cloud](/guide/cloud#developer) account has personal API tokens and a hosted
[MCP server](https://modelcontextprotocol.io/) at `cloud.frameos.net/api/mcp`, so Claude Code,
Claude Desktop, Cursor or any MCP client can list your frames, push scenes and settings, read logs,
build or edit scenes with the scene assistant, and render any scene server-side to see the result.
## Does it integrate with Home Assistant? [#does-it-integrate-with-home-assistant]
Three ways: the backend runs as a [Home Assistant add-on](/guide/backend#home-assistant-add-on);
the **HA Sensor** app pulls any Home Assistant entity into a scene; and HA automations can drive
frames through the [HTTP API](/guide/control) - change text, switch scenes, trigger renders.
## How do I share scenes or apps I made? [#how-do-i-share-scenes-or-apps-i-made]
Publish them on the [scene store](/guide/scene-store): fork, edit and preview in the browser
(or [have the AI build one](/guide/scene-store#ai) from a description), keep them private or make
them public, and anyone can install them on a cloud-managed frame with one click or on a
self-hosted backend by pasting a link. Scenes also export as JSON (and as ZIP
templates with preview images) you can share anywhere. The community hangs out on [Discord](https://discord.gg/9dT9y7EzUw) - show off your builds, get help,
and propose contributions. Code lives at
[github.com/FrameOS/frameos](https://github.com/FrameOS/frameos); PRs welcome.
## What does it cost? [#what-does-it-cost]
FrameOS is free and open source (AGPL-3.0). A complete frame is roughly the price of the panel
plus \~$20 of Pi: a 7.5" black/white build lands around $80, a 13.3" Spectra 6 color build around
$250.
## What does FrameOS Cloud cost? [#what-does-frameos-cloud-cost]
Nothing while in beta, with fixed limits (50 frames, 20 scenes per frame, 100 MB each of private
scenes, backups and logs) so one account can't crowd out the rest. Creating an account, enrolling
and managing frames, and installing public store scenes are meant to stay free; nothing
security-related will ever be paywalled. See [FrameOS Cloud](/guide/cloud#limits).
## Where are the terms of service, privacy policy and imprint? [#where-are-the-terms-of-service-privacy-policy-and-imprint]
On FrameOS Cloud: [Terms of Service](https://cloud.frameos.net/legal/terms),
[Privacy Policy](https://cloud.frameos.net/legal/privacy) and
[Imprint](https://cloud.frameos.net/legal/imprint). They're also linked in the footer of the
[homepage](/) and [blog](/blog). A self-hosted backend is yours alone - only the cloud, the scene
store and these websites are covered.
## Does this site set cookies? [#does-this-site-set-cookies]
No. These docs ask you nothing and store nothing on your device - no cookies, no local storage.
Page views are counted by PostHog in the EU in cookieless mode: it works out a one-way hash from
your IP address and browser on its servers, using a salt that's thrown away at the end of each day,
so the count can't be traced back to you or followed from one day to the next. We look at it to see
which pages are worth writing more of. FrameOS Cloud is a different matter - you have an account
there, so it asks before it turns analytics on, and you can change that answer from its footer.
# Elecrow CrowPanel 5.79" (/devices/elecrow-crowpanel-5in79)
The **Elecrow CrowPanel 5.79"** is a wide 792×272 four-grayscale e-ink board with an ESP32-S3
(8 MB PSRAM). The panel is driven by two SSD1683 controllers side by side; FrameOS handles
that for you. Its shape suits a status strip - departures, a now-playing bar, a single-line
dashboard.
FrameOS runs on it as a full [ESP32-S3 frame](/guide/esp32) - scenes render on the device. Pick
the **Elecrow CrowPanel 5.79"** hardware preset and the panel, GPIO map and all five front
buttons (HOME, EXIT, NEXT, OK, PREV) are configured for you. Buttons dispatch scene events, so
you can wire them to scene switches or anything else in the editor.
## Setup [#setup]
1. In the FrameOS backend, create a frame with **Flash embedded device**.
2. Choose **ESP32-S3**, select the **Elecrow CrowPanel 5.79"** preset, and enter your WiFi details.
3. Flash it over USB from the browser - the backend writes the release image for the board and sets
it up as this frame.
4. Install scenes as usual - they sync over WiFi, and the device updates itself over the air.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.**
Tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) if you try it.
# ESP32-C3 0.42" OLED board (/devices/esp32-c3-oled-0in42)
The **ESP32-C3 0.42" OLED board** - sold as HW-675, by 01Space, and under a dozen no-name labels -
is a thumb-sized **ESP32-C3** with a 72×40 monochrome SSD1306 OLED soldered on. It is the smallest
display FrameOS drives, and the cheapest way to try a microcontroller frame.
The C3 has no PSRAM, which is not enough for an on-device renderer - so FrameOS runs it as a
**thin client**: your [backend](/guide/backend) renders the scene and the device draws the finished
bitmap. The backend has to be reachable when the frame renders, and
[FrameOS Cloud](/guide/cloud) can't manage it. Everything else - scenes, apps, schedules, state
fields - works the same way as on an [ESP32-S3 frame](/guide/esp32).
Needs FrameOS **2026.9.10** or newer.
## Setup [#setup]
1. In the FrameOS backend, create a frame with **Flash embedded device**.
2. Choose **ESP32-C3**, select the **ESP32-C3 0.42" OLED dev board** preset, and enter your WiFi
details.
3. Flash it over USB from the browser - the backend writes the release image for the board and sets
it up as this frame.
4. Install scenes as usual. At 72×40 a clock, a number or a short line of text is about what fits.
The board has 4 MB of flash, so it uses the single-slot layout with no OTA partition:
firmware updates go over USB.
## Pin map [#pin-map]
| Signal | GPIO |
| ----------- | ---- |
| SDA | 5 |
| SCL | 6 |
| BOOT button | 9 |
The BOOT button is registered as a GPIO button and can trigger events in your
[scenes](/guide/scenes).
## Status [#status]
🟢 **Confirmed working** - a thin-client render at 72×40 was verified end to end on the board.
# Framebuffer & HDMI (/devices/framebuffer-hdmi)
The **HDMI / Framebuffer** driver renders straight to the Raspberry Pi's video output. Anything
you can plug into the HDMI port works: TVs, computer monitors, small HDMI LCD panels, projectors.
This is also the fast path: on a Raspberry Pi 5, FrameOS can push **60 frames per second** to an
HDMI display, while the same scene system also runs e-ink panels at one frame per minute.
## Setup [#setup]
1. Connect the display to the Pi's HDMI port.
2. Select the **HDMI / Framebuffer** driver when adding the frame.
3. Deploy. The width and height are autodetected from the display's mode - FrameOS writes them
into the frame's config and the backend picks them up, so only fill them in if you want to
force a different size.
On a Pi 5 with a Buildroot SD image, `/dev/fb0` may exist before the display has a mode set
(or while nothing is plugged in). The driver treats that as "not ready yet" and re-probes on its
own - every 2 seconds, backing off to a minute - instead of waiting for the next scheduled
render, so plugging a screen in brings the picture up within seconds even on an hourly frame.
## Power control [#power-control]
From 2026.9.18 the frame's **…** menu has **Turn display off** and **Turn display on** - in the
backend, the on-device admin panel and FrameOS Cloud. They send the same `turnOff` / `turnOn`
events a scene or schedule can. The same thing from the command line:
```bash
vcgencmd display_power 0 # off
vcgencmd display_power 1 # on
```
# Good Display 3.97" e-Paper (/devices/good-display-3in97)
The **Good Display 3.97" e-Paper** packs **800×480 pixels** into a small black-and-white panel -
about 230 pixels per inch, which is why text on it looks printed rather than pixelated. It's the
panel used by the [Seeed reTerminal Sticky](/devices/seeed-reterminal-sticky).
Like all e-paper, it holds its image with zero power; the frame only wakes to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_3in97`) compiled straight into the binary - no Python, no vendor libraries. Each
render draws your [scene](/guide/scenes) into a full-colour image, dithers it to black and white,
and pushes the result over SPI.
It also runs on the [ESP32-S3](/guide/esp32), where the same driver is compiled into the firmware.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi (or ESP32) over SPI using the bundled driver board and
ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Good Display 3.97" 800x480 Black/White** driver.
3. Save & deploy.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic with
panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw).
# HTTP upload (bring your own display) (/devices/http-upload)
**HTTP upload** pushes every rendered image to a URL of your choosing as an HTTP
POST - so you can drive displays FrameOS has never heard of. Anything that can receive an image
over the network becomes a FrameOS frame: custom microcontroller displays, digital signage
players, other servers, you name it.
## How it works [#how-it-works]
The frame renders your [scene](/guide/scenes) on its normal schedule. Instead of talking to a
panel over SPI or HDMI, the driver POSTs the rendered PNG to the endpoint you configure in the
frame's settings. Your receiver decides what to do with it.
## Setup [#setup]
1. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the **HTTP upload**
driver.
2. Set the width and height to match your target display, and configure the upload URL.
3. Save & deploy. Each render is delivered to your endpoint.
## Status [#status]
🟢 **Confirmed working** - tested on real hardware by the maintainers or the community.
# Device database (/devices)
FrameOS ships with **120+ display drivers**, selectable when you add a frame in the backend.
Native drivers compiled into the frame binary cover most e-ink panels by **Waveshare** and
**Pimoroni**, three generic drivers cover everything with a video output, and all-in-one
microcontroller boards get their own entries - every display below has its own page with full specs
and setup instructions.
## How the drivers work [#how-the-drivers-work]
* **E-ink panels** are driven over SPI by drivers compiled directly into the FrameOS binary -
no Python, no runtime libraries. Each Waveshare panel is one C driver (the vendor's, or a FrameOS
fork of it) that is bound into the Pi binary and linked into the ESP32 firmware alike, so both
targets behave the same. FrameOS renders your [scene](/guide/scenes), dithers it to the panel's
exact ink palette, and pushes the result to the display.
* **[HDMI / Framebuffer](/devices/framebuffer-hdmi)** renders straight to the Pi's video output -
TVs, monitors, any HDMI panel. On a Pi 5 it pushes 60 frames per second.
* **Pimoroni HyperPixel** LCDs - the [4.0"](/devices/pimoroni-hyperpixel-4-touch), the
[4.0" Square](/devices/pimoroni-hyperpixel-4-square-touch) and the
[2.1" Round](/devices/pimoroni-hyperpixel-2.1inch-round-480x480-lcd) - sit on the GPIO header and
get their pixels over DPI. A native driver sets them up on a Pi Zero through Pi 5, and touch
arrives in scenes as pointer events.
* **[Web only](/devices/web-only)** runs a frame with no physical display: view it in the
browser, perfect for trying FrameOS or kiosk setups.
* **[HTTP upload](/devices/http-upload)** POSTs each rendered image to a URL of your choosing -
drive displays FrameOS has never heard of.
* Older or unusual **Pimoroni Inky** boards fall back to a
[Python driver](/devices/pimoroni-inky-python) that auto-detects the panel.
* Panels marked **ESP32** also work without a Raspberry Pi, driven by the
[FrameOS ESP32 firmware](/guide/esp32): that's every Waveshare-family SPI e-paper panel except
the 10.3" IT8951 and the 12.48" family. Every supported driver is compiled into one firmware
image and the panel is picked at runtime, so switching panels needs no rebuild.
* **All-in-one boards** - a panel with a microcontroller already attached - have their own pages
naming the hardware preset to choose: the
[Waveshare PhotoPainter](/devices/waveshare-esp32-s3-photopainter) and
[13.3E6](/devices/waveshare-esp32-s3-epaper-13in3e6), the
[Seeed reTerminals](/devices/seeed-reterminal-e1001) (up to the
[13.3" E1004](/devices/seeed-reterminal-e1004)), the [TRMNL](/devices/trmnl-og), the
[XTEINK X4](/devices/xteink-x4), the [Elecrow CrowPanel](/devices/elecrow-crowpanel-5in79) and
the [Pimoroni Inky Frames](/devices/pimoroni-inky-frame-5-7).
## Picking a panel [#picking-a-panel]
New to e-ink? Start with the [Waveshare buying guide](/devices/waveshare-other-displays), or jump
straight to a crowd favourite: the [Pimoroni Inky Impression 7.3"](/devices/pimoroni-inky-impression-7.3inch-eink)
for plug-and-play color, or the [Waveshare 13.3" Spectra 6](/devices/waveshare-epd-13in3e) for a
big, vivid art frame.
**Spectra 6 or ACeP?** Both are color e-ink, but they look different side by side. Spectra 6 (E6)
is brighter with more saturated colors and a whiter white; the older 7-color ACeP panels have a
softer, slightly purple cast and a longer refresh. Same image, Spectra 6 on the left, ACeP on the
right:
## Your panel is missing? [#your-panel-is-missing]
* Displays not in this database: [open an issue](https://github.com/FrameOS/frameos/issues), or
try the closest driver - controller boards are often shared between panels.
* Tested a panel marked "should work"? Tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw).
* Each device page is a markdown file in
[`content/docs/devices/`](https://github.com/FrameOS/frameos-docs/tree/main/content/docs/devices),
so corrections and additions are one pull request away.
# Pimoroni HyperPixel 2.1" round LCD (/devices/pimoroni-hyperpixel-2.1inch-round-480x480-lcd)
* Manufacturer: [Pimoroni](https://shop.pimoroni.com/)
* Device: [HyperPixel 2.1" Round 480×480 LCD with touch](https://shop.pimoroni.com/products/hyperpixel-round)
* Extra: touch input
## What to buy [#what-to-buy]
* The HyperPixel 2.1" Round display from Pimoroni.
* A Raspberry Pi Zero 2 W (preferred) or Zero W.
* A 40-pin GPIO header to solder onto the Pi - or buy the pre-pinned Zero WH/W2H and skip the
soldering.
## How it works [#how-it-works]
One native driver runs the panel - no Pimoroni installer, no Python, no Bullseye. On the first
deploy its setup writes what the panel needs into `config.txt` and the frame reboots once. How the
pixels get to the glass depends on the Pi, which the frame detects by itself:
* **Pi Zero to Pi 4** - the Pi's firmware drives DPI into `/dev/fb0`, and FrameOS sends the panel
its init sequence and switches the backlight over GPIO.
* **Pi 5** (FrameOS 2026.9.20+) - a Pi 5 has no firmware DPI, so setup enables the kernel's
`vc4-kms-dpi-hyperpixel2r` overlay instead and FrameOS only writes the framebuffer. No touch on
this path, and it hasn't been tried on real hardware yet.
From FrameOS 2026.9.20 touch works on a Pi Zero to Pi 4: the kernel's own FT5x06 driver reads it,
and it arrives in your [scenes](/guide/scenes) as the pointer events `mouseMove`, `mouseDown` and
`mouseUp`. Coordinates follow the frame's rotate and flip settings.
## Setup [#setup]
1. Solder the header if your Pi needs one, and plug the display on.
2. [Add a frame](/guide/first-deploy) and select the **Pimoroni HyperPixel 2.1" Round** device.
3. Save & deploy. The first deploy reboots the Pi once, then the scene appears.
The HyperPixel takes every GPIO pin, so the Pi's I2C and SPI buses are switched off while it is
the frame's display - no other HATs or GPIO buttons alongside it.
### Coming from an older setup [#coming-from-an-older-setup]
Until FrameOS 2026.9.14 there were two devices for this panel: a legacy one on top of Pimoroni's
`hyperpixel2r` installer and Raspberry Pi OS Bullseye, and a "(native)" one. They are now the one
native driver behind the one device, and existing frames are moved over for you. The next full
deploy (or Buildroot update) comments out Pimoroni's overlay in `config.txt`, writes the DPI block
and reboots once. A frame from before touch gains two more `config.txt` lines and one reboot the
same way.
## Display power [#display-power]
**Turn display off / on** - in the frame's **…** menu, or as `turnOff` / `turnOn` events from a
scene or a [schedule](/guide/control) - switches the backlight. The frame keeps rendering while the
panel is off, so it comes back on with the current image. The driver owns the backlight pin
(GPIO 19) and only switches it on and off; there is no dimming.
## 3D models [#3d-models]
A community case design is available
[on Cults3D](https://cults3d.com/en/3d-model/gadget/enclosure-m3-for-pimoroni-hyperpixel-2-1-round-touch-and-raspberry-pi-zeer.imaero).
# Pimoroni HyperPixel 4.0" Square Touch (/devices/pimoroni-hyperpixel-4-square-touch)
The **Pimoroni HyperPixel 4.0" Square Touch** is a 4″ IPS LCD with a resolution of **720×720 pixels** in 18-bit colour and a capacitive touchscreen. It
sits on the Pi's 40-pin header and the pixels travel over DPI - no HDMI cable, and a refresh is
instant. That makes it a good fit for clocks, dashboards and anything that animates or reacts to a finger.
Pimoroni sells this board with and without touch, and as a rectangular 480×800 sibling - pick
the FrameOS device that matches yours: [HyperPixel 4.0"](/devices/pimoroni-hyperpixel-4), [HyperPixel 4.0" Touch](/devices/pimoroni-hyperpixel-4-touch), [HyperPixel 4.0" Square](/devices/pimoroni-hyperpixel-4-square). The
smaller round one is the [HyperPixel 2.1" Round](/devices/pimoroni-hyperpixel-2.1inch-round-480x480-lcd).
Needs FrameOS **2026.9.20** or newer.
## How it works [#how-it-works]
One native driver covers every HyperPixel 4.0 - no Pimoroni installer, no Python. On the first
deploy its setup writes what the panel needs into `config.txt` and the frame reboots once. How the
pixels get to the glass depends on the Pi, which the frame detects by itself:
* **Pi Zero to Pi 4** - the Pi's firmware drives DPI into `/dev/fb0`, and FrameOS sends the panel
its init sequence and switches the backlight over GPIO.
* **Pi 5** - a Pi 5 has no firmware DPI, so setup enables the kernel's `vc4-kms-dpi-hyperpixel4sq`
overlay instead and FrameOS only writes the framebuffer.
Touch is read by the kernel's own FT5x06 driver and arrives in your [scenes](/guide/scenes)
as the pointer events `mouseMove`, `mouseDown` and `mouseUp`. Coordinates follow the frame's
rotate and flip settings, so a scene on a rotated panel gets taps where it drew things.
## Setup [#setup]
1. Plug the display onto the Pi's GPIO header. On a Pi Zero that means soldering a header first -
or buy the pre-pinned WH / 2 WH.
2. [Add a frame](/guide/first-deploy) and select the **Pimoroni HyperPixel 4.0" Square Touch** device.
3. Save & deploy. The first deploy reboots the Pi once, then the scene appears.
The HyperPixel takes every GPIO pin, so the Pi's I2C and SPI buses are switched off while it is
the frame's display - no other HATs or GPIO buttons alongside it. **Turn display off / on** (from
the frame's menu, or on a [schedule](/guide/control)) switches the backlight; there is no dimming.
Moving the SD card to a different HyperPixel is fine: change the device, deploy, and setup swaps
the `config.txt` lines over.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver is confirmed on the
[HyperPixel 4.0" Touch](/devices/pimoroni-hyperpixel-4-touch) with a Pi 5; this variant uses
Pimoroni's own init sequence and timings but hasn't met a panel yet. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or
[Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Pimoroni HyperPixel 4.0" Square (/devices/pimoroni-hyperpixel-4-square)
The **Pimoroni HyperPixel 4.0" Square** is a 4″ IPS LCD with a resolution of **720×720 pixels** in 18-bit colour. It
sits on the Pi's 40-pin header and the pixels travel over DPI - no HDMI cable, and a refresh is
instant. That makes it a good fit for clocks, dashboards and anything that animates.
Pimoroni sells this board with and without touch, and as a rectangular 480×800 sibling - pick
the FrameOS device that matches yours: [HyperPixel 4.0"](/devices/pimoroni-hyperpixel-4), [HyperPixel 4.0" Touch](/devices/pimoroni-hyperpixel-4-touch), [HyperPixel 4.0" Square Touch](/devices/pimoroni-hyperpixel-4-square-touch). The
smaller round one is the [HyperPixel 2.1" Round](/devices/pimoroni-hyperpixel-2.1inch-round-480x480-lcd).
Needs FrameOS **2026.9.20** or newer.
## How it works [#how-it-works]
One native driver covers every HyperPixel 4.0 - no Pimoroni installer, no Python. On the first
deploy its setup writes what the panel needs into `config.txt` and the frame reboots once. How the
pixels get to the glass depends on the Pi, which the frame detects by itself:
* **Pi Zero to Pi 4** - the Pi's firmware drives DPI into `/dev/fb0`, and FrameOS sends the panel
its init sequence and switches the backlight over GPIO.
* **Pi 5** - a Pi 5 has no firmware DPI, so setup enables the kernel's `vc4-kms-dpi-hyperpixel4sq`
overlay instead and FrameOS only writes the framebuffer.
## Setup [#setup]
1. Plug the display onto the Pi's GPIO header. On a Pi Zero that means soldering a header first -
or buy the pre-pinned WH / 2 WH.
2. [Add a frame](/guide/first-deploy) and select the **Pimoroni HyperPixel 4.0" Square** device.
3. Save & deploy. The first deploy reboots the Pi once, then the scene appears.
The HyperPixel takes every GPIO pin, so the Pi's I2C and SPI buses are switched off while it is
the frame's display - no other HATs or GPIO buttons alongside it. **Turn display off / on** (from
the frame's menu, or on a [schedule](/guide/control)) switches the backlight; there is no dimming.
Moving the SD card to a different HyperPixel is fine: change the device, deploy, and setup swaps
the `config.txt` lines over.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver is confirmed on the
[HyperPixel 4.0" Touch](/devices/pimoroni-hyperpixel-4-touch) with a Pi 5; this variant uses
Pimoroni's own init sequence and timings but hasn't met a panel yet. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or
[Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Pimoroni HyperPixel 4.0" Touch (/devices/pimoroni-hyperpixel-4-touch)
The **Pimoroni HyperPixel 4.0" Touch** is a 4″ IPS LCD with a resolution of **480×800 pixels** in 18-bit colour and a capacitive touchscreen. It
sits on the Pi's 40-pin header and the pixels travel over DPI - no HDMI cable, and a refresh is
instant. That makes it a good fit for clocks, dashboards and anything that animates or reacts to a finger.
Pimoroni sells this board with and without touch, and as a square 720×720 sibling - pick
the FrameOS device that matches yours: [HyperPixel 4.0"](/devices/pimoroni-hyperpixel-4), [HyperPixel 4.0" Square](/devices/pimoroni-hyperpixel-4-square), [HyperPixel 4.0" Square Touch](/devices/pimoroni-hyperpixel-4-square-touch). The
smaller round one is the [HyperPixel 2.1" Round](/devices/pimoroni-hyperpixel-2.1inch-round-480x480-lcd).
Needs FrameOS **2026.9.20** or newer.
## How it works [#how-it-works]
One native driver covers every HyperPixel 4.0 - no Pimoroni installer, no Python. On the first
deploy its setup writes what the panel needs into `config.txt` and the frame reboots once. How the
pixels get to the glass depends on the Pi, which the frame detects by itself:
* **Pi Zero to Pi 4** - the Pi's firmware drives DPI into `/dev/fb0`, and FrameOS sends the panel
its init sequence and switches the backlight over GPIO.
* **Pi 5** - a Pi 5 has no firmware DPI, so setup enables the kernel's `vc4-kms-dpi-hyperpixel4`
overlay instead and FrameOS only writes the framebuffer.
Touch is read by the kernel's own Goodix GT911 driver and arrives in your [scenes](/guide/scenes)
as the pointer events `mouseMove`, `mouseDown` and `mouseUp`. Coordinates follow the frame's
rotate and flip settings, so a scene on a rotated panel gets taps where it drew things.
## Setup [#setup]
1. Plug the display onto the Pi's GPIO header. On a Pi Zero that means soldering a header first -
or buy the pre-pinned WH / 2 WH.
2. [Add a frame](/guide/first-deploy) and select the **Pimoroni HyperPixel 4.0" Touch** device.
3. Save & deploy. The first deploy reboots the Pi once, then the scene appears.
The panel scans in portrait, 480×800. For an 800×480 landscape frame set **Rotate** to 90 or 270
in the frame's settings.
The HyperPixel takes every GPIO pin, so the Pi's I2C and SPI buses are switched off while it is
the frame's display - no other HATs or GPIO buttons alongside it. **Turn display off / on** (from
the frame's menu, or on a [schedule](/guide/control)) switches the backlight; there is no dimming.
Moving the SD card to a different HyperPixel is fine: change the device, deploy, and setup swaps
the `config.txt` lines over.
## Status [#status]
🟢 **Confirmed working** on a Raspberry Pi 5 - picture, touch and display off / on. The Pi Zero to
Pi 4 path shares its design with the confirmed [2.1" Round](/devices/pimoroni-hyperpixel-2.1inch-round-480x480-lcd)
driver but hasn't been tried on this panel yet. If you run one, tell us on
[GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw).
# Pimoroni HyperPixel 4.0" (/devices/pimoroni-hyperpixel-4)
The **Pimoroni HyperPixel 4.0"** is a 4″ IPS LCD with a resolution of **480×800 pixels** in 18-bit colour. It
sits on the Pi's 40-pin header and the pixels travel over DPI - no HDMI cable, and a refresh is
instant. That makes it a good fit for clocks, dashboards and anything that animates.
Pimoroni sells this board with and without touch, and as a square 720×720 sibling - pick
the FrameOS device that matches yours: [HyperPixel 4.0" Touch](/devices/pimoroni-hyperpixel-4-touch), [HyperPixel 4.0" Square](/devices/pimoroni-hyperpixel-4-square), [HyperPixel 4.0" Square Touch](/devices/pimoroni-hyperpixel-4-square-touch). The
smaller round one is the [HyperPixel 2.1" Round](/devices/pimoroni-hyperpixel-2.1inch-round-480x480-lcd).
Needs FrameOS **2026.9.20** or newer.
## How it works [#how-it-works]
One native driver covers every HyperPixel 4.0 - no Pimoroni installer, no Python. On the first
deploy its setup writes what the panel needs into `config.txt` and the frame reboots once. How the
pixels get to the glass depends on the Pi, which the frame detects by itself:
* **Pi Zero to Pi 4** - the Pi's firmware drives DPI into `/dev/fb0`, and FrameOS sends the panel
its init sequence and switches the backlight over GPIO.
* **Pi 5** - a Pi 5 has no firmware DPI, so setup enables the kernel's `vc4-kms-dpi-hyperpixel4`
overlay instead and FrameOS only writes the framebuffer.
## Setup [#setup]
1. Plug the display onto the Pi's GPIO header. On a Pi Zero that means soldering a header first -
or buy the pre-pinned WH / 2 WH.
2. [Add a frame](/guide/first-deploy) and select the **Pimoroni HyperPixel 4.0"** device.
3. Save & deploy. The first deploy reboots the Pi once, then the scene appears.
The panel scans in portrait, 480×800. For an 800×480 landscape frame set **Rotate** to 90 or 270
in the frame's settings.
The HyperPixel takes every GPIO pin, so the Pi's I2C and SPI buses are switched off while it is
the frame's display - no other HATs or GPIO buttons alongside it. **Turn display off / on** (from
the frame's menu, or on a [schedule](/guide/control)) switches the backlight; there is no dimming.
Moving the SD card to a different HyperPixel is fine: change the device, deploy, and setup swaps
the `config.txt` lines over.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver is confirmed on the
[HyperPixel 4.0" Touch](/devices/pimoroni-hyperpixel-4-touch) with a Pi 5; this variant uses
Pimoroni's own init sequence and timings but hasn't met a panel yet. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or
[Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Pimoroni Inky Frame 4.0" (/devices/pimoroni-inky-frame-4)
The **Pimoroni Inky Frame 4.0"** is a 640×400 seven-colour ACeP e-ink display with a Raspberry Pi
Pico W soldered to the back. FrameOS runs on it as a [thin client](/guide/pico): the
[backend](/guide/backend) renders and dithers the scene, and the Pico streams the packed panel
bytes into the controller. A Pico W has 264 KB of RAM - far too little for an on-device
renderer, but plenty to stream a picture.
The board's own battery latch is supported, so `deep_sleep` powers it right down between refreshes
and cold-boots on the next interval or a button press.
## Setup [#setup]
1. Download `frameos--pico-w.uf2` from the
[latest release](https://github.com/FrameOS/frameos/releases).
2. Hold **BOOTSEL** while plugging in USB and copy the file onto the `RPI-RP2` drive.
3. Add a frame in the backend with **Flash embedded device** and the
**Pimoroni Inky Frame 4.0"** preset.
4. Provision it over the USB serial console - see the [Pico guide](/guide/pico).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** This panel's initialisation is derived from
the Waveshare 4.01" (F) reference; if yours comes up mirrored or offset,
[open an issue](https://github.com/FrameOS/frameos/issues).
# Pimoroni Inky Frame 5.7" (/devices/pimoroni-inky-frame-5-7)
The **Pimoroni Inky Frame 5.7"** is a 600×448 seven-colour ACeP display with a Raspberry Pi Pico W
on the back - the most common Inky Frame, and a lovely bit of hardware that has always been
limited by what fits on its microcontroller.
FrameOS runs it as a [thin client](/guide/pico): the [backend](/guide/backend) renders and dithers
the scene, and the Pico streams the packed panel bytes into the controller. So you get the full
FrameOS scene editor, apps and state fields on a board with 264 KB of RAM - as long as the
backend is reachable when the frame renders.
`deep_sleep` uses the board's RTC and power latch to shut it down completely between refreshes,
waking on the next interval or a front button.
## Setup [#setup]
1. Download `frameos--pico-w.uf2` from the
[latest release](https://github.com/FrameOS/frameos/releases).
2. Hold **BOOTSEL** while plugging in USB and copy the file onto the `RPI-RP2` drive.
3. Add a frame in the backend with **Flash embedded device** and the
**Pimoroni Inky Frame 5.7"** preset.
4. Provision it over the USB serial console - see the [Pico guide](/guide/pico).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.**
Tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) if you try it.
# Pimoroni Inky Frame 7.3" (Pico 2 W) (/devices/pimoroni-inky-frame-7-3-pico2)
The **2024 refresh of the Pimoroni Inky Frame 7.3"** keeps the 800×480 seven-colour ACeP panel and
swaps the Raspberry Pi Pico W for a **Pico 2 W** (RP2350). Same panel, different firmware build.
FrameOS runs it as a [thin client](/guide/pico): the [backend](/guide/backend) renders and dithers
the scene, and the Pico streams the packed panel bytes into the controller. The backend has to be
reachable when the frame renders.
## Setup [#setup]
1. Download `frameos--pico-2w.uf2` (note the **2w**) from the
[latest release](https://github.com/FrameOS/frameos/releases).
2. Hold **BOOTSEL** while plugging in USB and copy the file onto the `RP2350` drive.
3. Add a frame in the backend with **Flash embedded device** and the
**Pimoroni Inky Frame 7.3" (Pico 2 W, 2024)** preset.
4. Provision it over the USB serial console - see the [Pico guide](/guide/pico).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.**
Tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) if you try it.
# Pimoroni Inky Frame 7.3" Spectra 6 (/devices/pimoroni-inky-frame-7-3-spectra)
The **Spectra 6 Inky Frame 7.3"** (August 2025 onwards) replaces the seven-colour ACeP panel with
a **Spectra 6** one - deeper, more saturated colours and a much better black - on a Raspberry Pi
Pico 2 W. You can tell it apart from the older
[ACeP version](/devices/pimoroni-inky-frame-7-3-pico2) by the black border along the top of the
panel.
FrameOS runs it as a [thin client](/guide/pico): the [backend](/guide/backend) renders and dithers
the scene to the Spectra 6 palette, and the Pico streams the packed panel bytes into the
controller. The backend has to be reachable when the frame renders.
## Setup [#setup]
1. Download `frameos--pico-2w.uf2` (note the **2w**) from the
[latest release](https://github.com/FrameOS/frameos/releases).
2. Hold **BOOTSEL** while plugging in USB and copy the file onto the `RP2350` drive.
3. Add a frame in the backend with **Flash embedded device** and the
**Pimoroni Inky Frame 7.3" Spectra 6** preset.
4. Provision it over the USB serial console - see the [Pico guide](/guide/pico).
FrameOS ships several Spectra 6 palettes, so you can tune how saturated the render looks against
your particular panel.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The Spectra 6 driver itself is confirmed on
other boards.
Tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) if you try it.
# Pimoroni Inky Frame 7.3" (/devices/pimoroni-inky-frame-7-3)
The **Pimoroni Inky Frame 7.3"** is an 800×480 seven-colour ACeP display with a Raspberry Pi
Pico W on the back - the biggest of the original Inky Frames, and a natural art frame.
FrameOS runs it as a [thin client](/guide/pico): the [backend](/guide/backend) renders and dithers
the scene, and the Pico streams the packed panel bytes into the controller. The backend has to be
reachable when the frame renders.
If your board says **Pico 2 W** on the back, use the
[2024 refresh page](/devices/pimoroni-inky-frame-7-3-pico2) instead; if the panel has a black top
border it is the [Spectra 6 version](/devices/pimoroni-inky-frame-7-3-spectra).
## Setup [#setup]
1. Download `frameos--pico-w.uf2` from the
[latest release](https://github.com/FrameOS/frameos/releases).
2. Hold **BOOTSEL** while plugging in USB and copy the file onto the `RPI-RP2` drive.
3. Add a frame in the backend with **Flash embedded device** and the
**Pimoroni Inky Frame 7.3"** preset.
4. Provision it over the USB serial console - see the [Pico guide](/guide/pico).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.**
Tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) if you try it.
# Pimoroni Inky Impression 13.3" (2025 / Spectra 6) (/devices/pimoroni-inky-impression-13-2025)
The **Pimoroni Inky Impression 13.3" (2025 / Spectra 6)** is a 13.3″ e-ink panel with a resolution of
**1600×1200 pixels** showing **six Spectra 6 ink colors**. Spectra 6 is the current generation of color e-ink: six vivid ink colors with much better saturation than the older 7-color ACeP panels. A full refresh takes roughly 15-30 seconds.
Pimoroni's boards are the easiest e-ink hardware to start with: panel and driver electronics come
as one unit that plugs straight onto the Pi's GPIO header - no soldering, no separate driver board.
The four side buttons are registered automatically and can trigger events in your
[scenes](/guide/scenes) - switch scenes, cycle images, anything.
## How it works [#how-it-works]
FrameOS ships a [native Nim driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/inky)
(`pimoroni.inky_impression_13`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Plug the board onto the Pi's GPIO header.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Pimoroni Inky Impression - 13.3" 2025 edition** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Pimoroni Inky Impression 4.0" (2025 / Spectra 6) (/devices/pimoroni-inky-impression-4-2025)
The **Pimoroni Inky Impression 4.0" (2025 / Spectra 6)** is a 4″ e-ink panel with a resolution of
**600×400 pixels** showing **six Spectra 6 ink colors**. Spectra 6 is the current generation of color e-ink: six vivid ink colors with much better saturation than the older 7-color ACeP panels. A full refresh takes roughly 15-30 seconds.
Pimoroni's boards are the easiest e-ink hardware to start with: panel and driver electronics come
as one unit that plugs straight onto the Pi's GPIO header - no soldering, no separate driver board.
The four side buttons are registered automatically and can trigger events in your
[scenes](/guide/scenes) - switch scenes, cycle images, anything.
## How it works [#how-it-works]
FrameOS ships a [native Nim driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/inky)
(`pimoroni.inky_impression_4_2025`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Plug the board onto the Pi's GPIO header.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Pimoroni Inky Impression - 4.0" 2025 edition** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Pimoroni Inky Impression 4.0" (7-colour) (/devices/pimoroni-inky-impression-4-7-color)
The **Pimoroni Inky Impression 4.0" (7-colour)** is a 4″ e-ink panel with a resolution of
**640×400 pixels** showing **seven ACeP ink colors**. ACeP ("Advanced Color ePaper") packs seven real ink colors into every pixel. A full refresh takes around 30 seconds, and the palette is softer than a backlit screen - ideal for photos and generative art with a paper-like look.
Pimoroni's boards are the easiest e-ink hardware to start with: panel and driver electronics come
as one unit that plugs straight onto the Pi's GPIO header - no soldering, no separate driver board.
The four side buttons are registered automatically and can trigger events in your
[scenes](/guide/scenes) - switch scenes, cycle images, anything.
## How it works [#how-it-works]
FrameOS ships a [native Nim driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/inky)
(`pimoroni.inky_impression_4_7_color`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Plug the board onto the Pi's GPIO header.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Pimoroni Inky Impression - 4.0" 7-colour** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Pimoroni Inky Impression 5.7" 7-colour (/devices/pimoroni-inky-impression-5.7inch-eink)
* Manufacturer: [Pimoroni](https://shop.pimoroni.com/)
* Device: [Inky Impression 5.7"](https://shop.pimoroni.com/products/inky-impression-5-7?variant=32298701324371)
* Extra: 4 physical buttons (usable as scene event triggers in FrameOS)
The Inky Impression line is the easiest e-ink hardware to start with: the panel comes glued to a
HAT that plugs straight onto the Pi's GPIO header. No soldering, no separate driver board.
## Setup [#setup]
1. Plug the HAT onto the Pi, flash the SD card, [add the frame](/guide/first-deploy) in the
backend.
2. Select the **Pimoroni Inky Impression - 5.7" 7-colour** driver.
3. Save & deploy. The display blinks through its refresh cycle and shows your scene.
Recent Inky Impression models have native Nim drivers in FrameOS (fast, no extra dependencies).
Older or unusual Inky models can use the **Python driver** fallback, which installs Pimoroni's
Python library during the first deploy.
The four side buttons are registered automatically and can trigger events in your scenes - switch
scenes, cycle images, anything.
# Pimoroni Inky Impression 7.3" (2025 / Spectra 6) (/devices/pimoroni-inky-impression-7-2025)
The **Pimoroni Inky Impression 7.3" (2025 / Spectra 6)** is a 7.3″ e-ink panel with a resolution of
**800×480 pixels** showing **six Spectra 6 ink colors**. Spectra 6 is the current generation of color e-ink: six vivid ink colors with much better saturation than the older 7-color ACeP panels. A full refresh takes roughly 15-30 seconds.
Pimoroni's boards are the easiest e-ink hardware to start with: panel and driver electronics come
as one unit that plugs straight onto the Pi's GPIO header - no soldering, no separate driver board.
The four side buttons are registered automatically and can trigger events in your
[scenes](/guide/scenes) - switch scenes, cycle images, anything.
## How it works [#how-it-works]
FrameOS ships a [native Nim driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/inky)
(`pimoroni.inky_impression_7`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Plug the board onto the Pi's GPIO header.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Pimoroni Inky Impression - 7.3" 2025 edition** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Pimoroni Inky Impression 7.3" 7-colour (/devices/pimoroni-inky-impression-7.3inch-eink)
* Manufacturer: [Pimoroni](https://shop.pimoroni.com/)
* Device: [Inky Impression 7.3"](https://shop.pimoroni.com/products/inky-impression-7-3)
* Extra: 4 physical buttons (usable as scene event triggers in FrameOS)
Like its 5.7" sibling, the 7.3" Inky Impression is plug-and-play: panel and driver board are one
unit that slots onto the GPIO header. Both the original 7-colour version and the **2025 Spectra 6
edition** are supported with native drivers - there's even a driver for the 13.3" 2025 edition.
## Setup [#setup]
1. Plug the HAT onto the Pi, flash the SD card, [add the frame](/guide/first-deploy) in the
backend.
2. Select the matching **Pimoroni Inky Impression 7.3"** driver (original or 2025 edition).
3. Save & deploy. The display blinks through its refresh cycle and shows your scene.
For a full build walkthrough - including 3D-printed cases for these panels - watch the
[Spectra 6 build video](/blog/eink-spectra-waveshare-pimoroni).
# Pimoroni Inky Impression - other models (Python driver) (/devices/pimoroni-inky-impression-python)
The **Pimoroni Inky Impression - other models (Python driver)** driver is a catch-all for Inky panels that don't have a dedicated
native driver in FrameOS. It uses [Pimoroni's official Python library](https://github.com/pimoroni/inky),
which auto-detects most Inky boards via the EEPROM on the HAT.
## How it works [#how-it-works]
On the first deploy, FrameOS installs the Inky Python library on the Raspberry Pi and renders
through a small helper process. This is slower to deploy and uses a bit more memory than the
native Nim drivers, but it supports every panel the official library supports - resolution and
colors are detected from the board itself.
If your panel has a dedicated entry in the [device database](/devices), prefer that driver.
## Setup [#setup]
1. Plug the board onto the Pi's GPIO header.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Pimoroni Inky Impression - all others (Python driver)** driver.
3. Save & deploy. The first deploy takes longer while the Python dependencies install.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Pimoroni Inky pHAT 2.13" (4-colour) (/devices/pimoroni-inky-phat-4)
The **Pimoroni Inky pHAT 2.13" (4-colour)** is a 2.13″ e-ink panel with a resolution of
**250×122 pixels** showing **black, white, yellow and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Pimoroni's boards are the easiest e-ink hardware to start with: panel and driver electronics come
as one unit that plugs straight onto the Pi's GPIO header - no soldering, no separate driver board.
## How it works [#how-it-works]
FrameOS ships a [native Nim driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/inky)
(`pimoroni.inky_phat_4`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Plug the board onto the Pi's GPIO header.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Pimoroni Inky pHAT - 2.13" 4-colour** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Pimoroni Inky pHAT 2.13" black/white (/devices/pimoroni-inky-phat-black)
The **Pimoroni Inky pHAT 2.13" black/white** is a 2.13″ e-ink panel with a resolution of
**212×104 pixels** showing **black and white**. Two-tone e-ink has the fastest refresh of any e-paper type - a full update takes only a couple of seconds - and the high contrast works great for text, dashboards and dithered photos.
Pimoroni's boards are the easiest e-ink hardware to start with: panel and driver electronics come
as one unit that plugs straight onto the Pi's GPIO header - no soldering, no separate driver board.
## How it works [#how-it-works]
FrameOS ships a [native Nim driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/inky)
(`pimoroni.inky_phat_black`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Plug the board onto the Pi's GPIO header.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Pimoroni Inky pHAT - 2.13" black/white** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Pimoroni Inky pHAT 2.13" black/white/red (high-temp) (/devices/pimoroni-inky-phat-red-ht)
The **Pimoroni Inky pHAT 2.13" black/white/red (high-temp)** is a 2.13″ e-ink panel with a resolution of
**212×104 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Pimoroni's boards are the easiest e-ink hardware to start with: panel and driver electronics come
as one unit that plugs straight onto the Pi's GPIO header - no soldering, no separate driver board.
## How it works [#how-it-works]
FrameOS ships a [native Nim driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/inky)
(`pimoroni.inky_phat_red_ht`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Plug the board onto the Pi's GPIO header.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Pimoroni Inky pHAT - 2.13" black/white/red high-temp** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Pimoroni Inky pHAT 2.13" black/white/red (/devices/pimoroni-inky-phat-red)
The **Pimoroni Inky pHAT 2.13" black/white/red** is a 2.13″ e-ink panel with a resolution of
**212×104 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Pimoroni's boards are the easiest e-ink hardware to start with: panel and driver electronics come
as one unit that plugs straight onto the Pi's GPIO header - no soldering, no separate driver board.
## How it works [#how-it-works]
FrameOS ships a [native Nim driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/inky)
(`pimoroni.inky_phat_red`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Plug the board onto the Pi's GPIO header.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Pimoroni Inky pHAT - 2.13" black/white/red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Pimoroni Inky pHAT 2.13" black/white (SSD1608) (/devices/pimoroni-inky-phat-ssd1608-black)
The **Pimoroni Inky pHAT 2.13" black/white (SSD1608)** is a 2.13″ e-ink panel with a resolution of
**250×122 pixels** showing **black and white**. Two-tone e-ink has the fastest refresh of any e-paper type - a full update takes only a couple of seconds - and the high contrast works great for text, dashboards and dithered photos.
Pimoroni's boards are the easiest e-ink hardware to start with: panel and driver electronics come
as one unit that plugs straight onto the Pi's GPIO header - no soldering, no separate driver board.
## How it works [#how-it-works]
FrameOS ships a [native Nim driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/inky)
(`pimoroni.inky_phat_ssd1608_black`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Plug the board onto the Pi's GPIO header.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Pimoroni Inky pHAT - 2.13" black/white (SSD1608)** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Pimoroni Inky pHAT 2.13" black/white/red (SSD1608) (/devices/pimoroni-inky-phat-ssd1608-red)
The **Pimoroni Inky pHAT 2.13" black/white/red (SSD1608)** is a 2.13″ e-ink panel with a resolution of
**250×122 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Pimoroni's boards are the easiest e-ink hardware to start with: panel and driver electronics come
as one unit that plugs straight onto the Pi's GPIO header - no soldering, no separate driver board.
## How it works [#how-it-works]
FrameOS ships a [native Nim driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/inky)
(`pimoroni.inky_phat_ssd1608_red`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Plug the board onto the Pi's GPIO header.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Pimoroni Inky pHAT - 2.13" black/white/red (SSD1608)** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Pimoroni Inky pHAT 2.13" black/white/yellow (SSD1608) (/devices/pimoroni-inky-phat-ssd1608-yellow)
The **Pimoroni Inky pHAT 2.13" black/white/yellow (SSD1608)** is a 2.13″ e-ink panel with a resolution of
**250×122 pixels** showing **black, white and yellow**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Pimoroni's boards are the easiest e-ink hardware to start with: panel and driver electronics come
as one unit that plugs straight onto the Pi's GPIO header - no soldering, no separate driver board.
## How it works [#how-it-works]
FrameOS ships a [native Nim driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/inky)
(`pimoroni.inky_phat_ssd1608_yellow`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Plug the board onto the Pi's GPIO header.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Pimoroni Inky pHAT - 2.13" black/white/yellow (SSD1608)** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Pimoroni Inky pHAT 2.13" black/white/yellow (/devices/pimoroni-inky-phat-yellow)
The **Pimoroni Inky pHAT 2.13" black/white/yellow** is a 2.13″ e-ink panel with a resolution of
**212×104 pixels** showing **black, white and yellow**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Pimoroni's boards are the easiest e-ink hardware to start with: panel and driver electronics come
as one unit that plugs straight onto the Pi's GPIO header - no soldering, no separate driver board.
## How it works [#how-it-works]
FrameOS ships a [native Nim driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/inky)
(`pimoroni.inky_phat_yellow`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Plug the board onto the Pi's GPIO header.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Pimoroni Inky pHAT - 2.13" black/white/yellow** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
## Status [#status]
🟢 **Confirmed working** - tested on real hardware by the maintainers or the community.
# Pimoroni Inky - other models (Python driver) (/devices/pimoroni-inky-python)
The **Pimoroni Inky - other models (Python driver)** driver is a catch-all for Inky panels that don't have a dedicated
native driver in FrameOS. It uses [Pimoroni's official Python library](https://github.com/pimoroni/inky),
which auto-detects most Inky boards via the EEPROM on the HAT.
## How it works [#how-it-works]
On the first deploy, FrameOS installs the Inky Python library on the Raspberry Pi and renders
through a small helper process. This is slower to deploy and uses a bit more memory than the
native Nim drivers, but it supports every panel the official library supports - resolution and
colors are detected from the board itself.
If your panel has a dedicated entry in the [device database](/devices), prefer that driver.
## Setup [#setup]
1. Plug the board onto the Pi's GPIO header.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Pimoroni Inky other (Python driver)** driver.
3. Save & deploy. The first deploy takes longer while the Python dependencies install.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Pimoroni Inky wHAT 4.2" (4-colour) (/devices/pimoroni-inky-what-4)
The **Pimoroni Inky wHAT 4.2" (4-colour)** is a 4.2″ e-ink panel with a resolution of
**400×300 pixels** showing **black, white, yellow and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Pimoroni's boards are the easiest e-ink hardware to start with: panel and driver electronics come
as one unit that plugs straight onto the Pi's GPIO header - no soldering, no separate driver board.
## How it works [#how-it-works]
FrameOS ships a [native Nim driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/inky)
(`pimoroni.inky_what_4`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Plug the board onto the Pi's GPIO header.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Pimoroni Inky wHAT - 4.2" 4-colour** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Pimoroni Inky wHAT 4.2" black/white (/devices/pimoroni-inky-what-black)
The **Pimoroni Inky wHAT 4.2" black/white** is a 4.2″ e-ink panel with a resolution of
**400×300 pixels** showing **black and white**. Two-tone e-ink has the fastest refresh of any e-paper type - a full update takes only a couple of seconds - and the high contrast works great for text, dashboards and dithered photos.
Pimoroni's boards are the easiest e-ink hardware to start with: panel and driver electronics come
as one unit that plugs straight onto the Pi's GPIO header - no soldering, no separate driver board.
## How it works [#how-it-works]
FrameOS ships a [native Nim driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/inky)
(`pimoroni.inky_what_black`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Plug the board onto the Pi's GPIO header.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Pimoroni Inky wHAT - 4.2" black/white** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Pimoroni Inky wHAT 4.2" black/white/red (high-temp) (/devices/pimoroni-inky-what-red-ht)
The **Pimoroni Inky wHAT 4.2" black/white/red (high-temp)** is a 4.2″ e-ink panel with a resolution of
**400×300 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Pimoroni's boards are the easiest e-ink hardware to start with: panel and driver electronics come
as one unit that plugs straight onto the Pi's GPIO header - no soldering, no separate driver board.
## How it works [#how-it-works]
FrameOS ships a [native Nim driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/inky)
(`pimoroni.inky_what_red_ht`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Plug the board onto the Pi's GPIO header.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Pimoroni Inky wHAT - 4.2" black/white/red high-temp** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Pimoroni Inky wHAT 4.2" black/white/red (/devices/pimoroni-inky-what-red)
The **Pimoroni Inky wHAT 4.2" black/white/red** is a 4.2″ e-ink panel with a resolution of
**400×300 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Pimoroni's boards are the easiest e-ink hardware to start with: panel and driver electronics come
as one unit that plugs straight onto the Pi's GPIO header - no soldering, no separate driver board.
## How it works [#how-it-works]
FrameOS ships a [native Nim driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/inky)
(`pimoroni.inky_what_red`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Plug the board onto the Pi's GPIO header.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Pimoroni Inky wHAT - 4.2" black/white/red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Pimoroni Inky wHAT 4.2" black/white (SSD1683) (/devices/pimoroni-inky-what-ssd1683-black)
The **Pimoroni Inky wHAT 4.2" black/white (SSD1683)** is a 4.2″ e-ink panel with a resolution of
**400×300 pixels** showing **black and white**. Two-tone e-ink has the fastest refresh of any e-paper type - a full update takes only a couple of seconds - and the high contrast works great for text, dashboards and dithered photos.
Pimoroni's boards are the easiest e-ink hardware to start with: panel and driver electronics come
as one unit that plugs straight onto the Pi's GPIO header - no soldering, no separate driver board.
## How it works [#how-it-works]
FrameOS ships a [native Nim driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/inky)
(`pimoroni.inky_what_ssd1683_black`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Plug the board onto the Pi's GPIO header.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Pimoroni Inky wHAT - 4.2" black/white (SSD1683)** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Pimoroni Inky wHAT 4.2" black/white/red (SSD1683) (/devices/pimoroni-inky-what-ssd1683-red)
The **Pimoroni Inky wHAT 4.2" black/white/red (SSD1683)** is a 4.2″ e-ink panel with a resolution of
**400×300 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Pimoroni's boards are the easiest e-ink hardware to start with: panel and driver electronics come
as one unit that plugs straight onto the Pi's GPIO header - no soldering, no separate driver board.
## How it works [#how-it-works]
FrameOS ships a [native Nim driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/inky)
(`pimoroni.inky_what_ssd1683_red`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Plug the board onto the Pi's GPIO header.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Pimoroni Inky wHAT - 4.2" black/white/red (SSD1683)** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Pimoroni Inky wHAT 4.2" black/white/yellow (SSD1683) (/devices/pimoroni-inky-what-ssd1683-yellow)
The **Pimoroni Inky wHAT 4.2" black/white/yellow (SSD1683)** is a 4.2″ e-ink panel with a resolution of
**400×300 pixels** showing **black, white and yellow**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Pimoroni's boards are the easiest e-ink hardware to start with: panel and driver electronics come
as one unit that plugs straight onto the Pi's GPIO header - no soldering, no separate driver board.
## How it works [#how-it-works]
FrameOS ships a [native Nim driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/inky)
(`pimoroni.inky_what_ssd1683_yellow`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Plug the board onto the Pi's GPIO header.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Pimoroni Inky wHAT - 4.2" black/white/yellow (SSD1683)** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Pimoroni Inky wHAT 4.2" black/white/yellow (/devices/pimoroni-inky-what-yellow)
The **Pimoroni Inky wHAT 4.2" black/white/yellow** is a 4.2″ e-ink panel with a resolution of
**400×300 pixels** showing **black, white and yellow**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Pimoroni's boards are the easiest e-ink hardware to start with: panel and driver electronics come
as one unit that plugs straight onto the Pi's GPIO header - no soldering, no separate driver board.
## How it works [#how-it-works]
FrameOS ships a [native Nim driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/inky)
(`pimoroni.inky_what_yellow`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Plug the board onto the Pi's GPIO header.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Pimoroni Inky wHAT - 4.2" black/white/yellow** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Seeed reTerminal E1001 (/devices/seeed-reterminal-e1001)
The **Seeed reTerminal E1001** is an all-in-one 7.5" black/white e-ink terminal built around an
ESP32-S3 with 8 MB of PSRAM and 32 MB of flash. FrameOS runs on it as a full
[ESP32-S3 frame](/guide/esp32): scenes render on the device, not on a server.
Pick the **Seeed reTerminal E1001** hardware preset when you add the frame and the panel, GPIO
map, flash profile and the three front buttons (REFRESH, LEFT, RIGHT) are configured for you. The
buttons dispatch scene events, so they can switch scenes or trigger a render.
## Setup [#setup]
1. In the FrameOS backend, create a frame with **Flash embedded device**.
2. Choose **ESP32-S3**, select the **Seeed reTerminal E1001** preset, and enter your WiFi details.
3. Flash it over USB from the browser - the backend writes the release image for the board and sets
it up as this frame. The E1001's USB-C port goes to a
CH340 UART bridge, not to the chip's own USB pins, so it shows up as a *USB Single Serial* port -
supported since firmware 2026.8.38 (earlier flashers refused it). On macOS install WCH's
[CH34x driver](/devices/seeed-reterminal-e1004#macos-install-the-wch-serial-driver-first)
first or the port never appears.
4. Install scenes as usual - they sync over WiFi, and the device updates itself over the air.
The preset also carries the E-series battery wiring (a 2:1 divider on GPIO 1, switched by
GPIO 21 - the same as the [E1004](/devices/seeed-reterminal-e1004), where it is verified), so a
cell on the battery header shows its charge in the backend or cloud, and the
[deep sleep options](/guide/esp32#power) apply.
See the [ESP32 guide](/guide/esp32) for what a microcontroller frame can and cannot do.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The preset and panel driver are in place,
but nobody has reported back from this exact board yet. If you have one,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw).
# Seeed reTerminal E1002 (/devices/seeed-reterminal-e1002)
The **Seeed reTerminal E1002** is the colour sibling of the
[E1001](/devices/seeed-reterminal-e1001): the same ESP32-S3 terminal, with a 7.3" **Spectra 6**
six-colour panel instead of black and white. (Want it bigger? The
[E1004](/devices/seeed-reterminal-e1004) is the 13.3" version.) FrameOS runs on it as a full
[ESP32-S3 frame](/guide/esp32), rendering and dithering scenes on the device.
Pick the **Seeed reTerminal E1002** hardware preset when you add the frame; the panel, GPIO map,
flash profile and the three front buttons (REFRESH, LEFT, RIGHT) come with it. The buttons
dispatch scene events, so they can switch scenes or trigger a render.
Spectra 6 panels take roughly half a minute per refresh - normal for the technology, and worth
knowing before you plan anything that updates often.
## Setup [#setup]
1. In the FrameOS backend, create a frame with **Flash embedded device**.
2. Choose **ESP32-S3**, select the **Seeed reTerminal E1002** preset, and enter your WiFi details.
3. Flash it over USB from the browser - the backend writes the release image for the board and sets
it up as this frame. The E1002's USB-C port goes to a
CH340 UART bridge, not to the chip's own USB pins, so it shows up as a *USB Single Serial* port -
supported since firmware 2026.8.38 (earlier flashers refused it). On macOS install WCH's
[CH34x driver](/devices/seeed-reterminal-e1004#macos-install-the-wch-serial-driver-first)
first or the port never appears.
4. Install scenes as usual - they sync over WiFi, and the device updates itself over the air.
The preset also carries the E-series battery wiring (a 2:1 divider on GPIO 1, switched by
GPIO 21 - the same as the [E1004](/devices/seeed-reterminal-e1004), where it is verified), so a
cell on the battery header shows its charge in the backend or cloud, and the
[deep sleep options](/guide/esp32#power) apply.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The Spectra 6 driver is confirmed on other
boards; this specific terminal has not been reported back yet.
Tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) if you try it.
# Seeed reTerminal E1004 (/devices/seeed-reterminal-e1004)
The **Seeed reTerminal E1004** is the big one in the reTerminal E-series: a portrait 13.3"
**Spectra 6** six-colour panel at 1200×1600, an ESP32-S3 with 8 MB of PSRAM and 32 MB of
flash, a microSD slot, a battery, and touch buttons on the bezel plus physical ones on the back.
FrameOS runs on it as a full [ESP32-S3 frame](/guide/esp32), rendering and dithering scenes on the
device.
Pick the **Seeed reTerminal E1004** hardware preset (`seeed_reterminal_e1004`) when you add the
frame. It selects the `waveshare.EPD_13in3e` panel driver with the T133A01 refresh sequence this
glass wants (about 30 seconds per refresh), the E-series SPI bus with the 13.3" panel's second chip
select, the 32 MB flash profile, the TF card asset mount, the three E-series keys (GPIO 3, 4
and 5, dispatched as scene events) and the battery wiring: a 2:1 divider on GPIO 1 that GPIO 21
switches on for each reading. The panel is the same glass as the
[Waveshare 13.3" (E)](/devices/waveshare-epd-13in3e), with Seeed's own analogue tuning applied by
the preset.
## Battery [#battery]
The E1004 carries a 5000 mAh cell, and the preset treats it as a battery frame from the start:
`deep_sleep_on_battery` is on with a 15-minute `wake_check`, so unplugged it deep-sleeps between
renders (waking on the timer, or on any of its buttons) and on USB it stays connected. The cloud or
backend shows **asleep · wakes in 5 min** and the charge while it's down, and delivers anything you
deploy on the next wake. A 15-minute weather scene keeps it awake for roughly 80-90 seconds per
cycle - render, dither and the panel's own refresh - which is where the battery goes; see
[Battery and deep sleep](/guide/esp32#power). A frame flashed before the preset knew the wiring
reads no battery until you re-run `set hardware seeed_reterminal_e1004` on the console or fill in
the three battery fields under **Power**.
## Why 8 MB is enough [#why-8-mb-is-enough]
A 1200×1600 RGBA canvas alone would be 7.3 MB - most of this board's PSRAM before a single
image is decoded. On this board the ESP32 renderer instead composites into a **16-bit canvas**
(3.7 MB), reserved once at boot and reused for every render, with dithered stores so gradients
stay smooth, and the packed framebuffer is reserved at boot as well so a fragmented heap can't fail
a render. Every panel the firmware drives is a dithered e-paper, so side by side with a full-colour
canvas a photo dithered to Spectra 6 lands the same palette within 0.05% per colour. Images decode
straight off the socket into that canvas - a 2 MB gallery PNG renders with 1.5 MB of
PSRAM to spare. That is what makes this board render on-device rather than as a thin client.
## Setup [#setup]
1. In the FrameOS backend, create a frame with **Flash embedded device**.
2. Choose **ESP32-S3**, select the **Seeed reTerminal E1004** preset, and enter your WiFi details.
3. Flash it over USB from the browser - the backend writes the release image for the board and sets
it up as this frame.
4. Install scenes as usual - they sync over WiFi, and the device updates itself over the air.
### macOS: install the WCH serial driver first [#macos-install-the-wch-serial-driver-first]
The E1004's USB port sits behind a WCH **CH340/CH341** USB-serial chip, and unlike most ESP32-S3
boards it does **not** show up as a serial port on macOS out of the box - the browser flasher won't
see the device until you install WCH's driver:
1. Download the driver from WCH's site:
[CH34XSER\_MAC](https://www.wch-ic.com/downloads/CH34XSER_MAC_ZIP.html), and run
`CH34xVCPDriver.pkg` from the archive.
2. Enable the extension under **System Settings → General → Login Items & Extensions →
Driver Extensions** and switch **CH34xVCPDriver** on. If macOS blocks it, go to
**Privacy & Security**, find the blocked WCH software and click **Allow**.
3. Reconnect the terminal and check that `ls /dev/tty.wch*` lists a
`/dev/tty.wchusbserial…` port. That's the one to pick in the flasher - it shows up as a
*USB Single Serial* port, which the firmware's console has answered on since 2026.8.38.
Windows 11 and Linux ship with the driver; Windows 10 and older need the
[CH341SER](https://www.wch-ic.com/downloads/CH341SER_ZIP.html) package. See the
[Seeed wiki](https://wiki.seeedstudio.com/getting_started_with_reterminal_e1004/) for the full
walkthrough.
A full Spectra 6 refresh on a panel this size takes around half a minute, and a heavy scene can
take a similar time to render and dither first. Fine for a frame that updates a few times an hour.
## Pin map [#pin-map]
| Signal | GPIO |
| ------ | ---- |
| CS | 10 |
| CS2 | 2 |
| DC | 11 |
| RST | 38 |
| BUSY | 13 |
| SCK | 7 |
| MOSI | 9 |
| PWR | 12 |
## Status [#status]
🟢 **Confirmed working** - runs on the maintainers' own E1004, on battery, managed from
[FrameOS Cloud](/guide/cloud): panel refresh, buttons, battery reading and deep sleep are all
verified on this hardware.
# Seeed reTerminal Sticky (/devices/seeed-reterminal-sticky)
The **Seeed reTerminal Sticky** is a pocket-sized 3.97" e-ink board with an ESP32-S3
(8 MB PSRAM, 32 MB flash) - 800×480 pixels on a small panel, so text stays crisp. It
makes a good desk or fridge frame: a day's agenda, a shopping list, the next bus.
FrameOS runs on it as a full [ESP32-S3 frame](/guide/esp32) - scenes render on the device.
Pick the **Seeed reTerminal Sticky** hardware preset and the panel, GPIO map, flash profile and
the POWER button are configured for you.
## Setup [#setup]
1. In the FrameOS backend, create a frame with **Flash embedded device**.
2. Choose **ESP32-S3**, select the **Seeed reTerminal Sticky** preset, and enter your WiFi details.
3. Flash it over USB from the browser - the backend writes the release image for the board and sets
it up as this frame.
4. Install scenes as usual - they sync over WiFi, and the device updates itself over the air.
Small panel, small battery: this is a good candidate for
[deep sleep between refreshes](/guide/esp32#power).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.**
Tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) if you try it.
# TRMNL 4.26" DIY Kit (/devices/trmnl-4in26-diy-kit)
The **TRMNL 4.26" DIY Kit** pairs a 4.26" 800×480 four-grayscale e-ink panel with a Seeed XIAO
ESP32-S3 on the XIAO ePaper driver board. 800×480 on a small panel means unusually crisp text, and
grayscale dithers photos noticeably better than a pure black/white panel.
With 8 MB of PSRAM on the XIAO S3, this kit runs the **full on-device renderer**. Pick the
**TRMNL 4.26" DIY Kit** hardware preset when you add the frame and the panel, GPIO map, flash
profile and the two buttons are set up for you.
## Setup [#setup]
1. In the FrameOS backend, create a frame with **Flash embedded device**.
2. Choose **ESP32-S3**, select the **TRMNL 4.26" DIY Kit** preset, and enter your WiFi details.
3. Flash it over USB from the browser - the backend writes the release image for the board and sets
it up as this frame.
4. Install scenes as usual - they sync over WiFi, and the device updates itself over the air.
Flashing FrameOS replaces the TRMNL firmware on the board, and needs no TRMNL account - the frame
talks to your own [backend](/guide/backend).
## Battery [#battery]
The XIAO ePaper driver board's battery connector is read through a 2:1 divider on GPIO 1, switched on by GPIO 6 - the preset carries the wiring (taken from the TRMNL firmware, not yet verified on this kit), so a cell shows its charge and the [deep sleep options](/guide/esp32#power) apply.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.**
Tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) if you try it.
# TRMNL BWRY (/devices/trmnl-bwry)
The **TRMNL BWRY** is the four-colour (black, white, yellow, red) 7.5" TRMNL device on an
**ESP32-C3**. Four-colour e-ink gives you headlines and highlights without the long refresh of a
six-colour Spectra panel.
Like the [TRMNL OG](/devices/trmnl-og), the C3 has no PSRAM, so FrameOS runs this board as a
**thin client**: your [backend](/guide/backend) renders and dithers the scene to the panel's
four-colour palette, and the device streams the packed result into the controller. The backend
therefore has to be reachable when the frame renders.
## Setup [#setup]
1. In the FrameOS backend, create a frame with **Flash embedded device**.
2. Choose **ESP32-C3**, select the **TRMNL BWRY** preset, and enter your WiFi details.
3. Flash it over USB from the browser - the backend writes the release image for the board and sets
it up as this frame.
4. Install scenes as usual.
Flashing FrameOS replaces the TRMNL firmware, and needs no TRMNL account or servers. The C3 image
uses a 4 MB single-slot layout with no OTA partition, so firmware updates go over USB.
## Battery [#battery]
The TRMNL's cell is read through a 2:1 divider on GPIO 3; the preset carries the wiring (from the TRMNL firmware, not yet verified under FrameOS), so its charge shows in the backend's metrics.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.**
Tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) if you try it.
# TRMNL 7.5" DIY Kit (/devices/trmnl-og-diy-kit)
The **TRMNL 7.5" DIY Kit** pairs a 7.5" 800×480 black/white e-ink panel with a Seeed XIAO
ESP32-S3 on the XIAO ePaper driver board. Because the XIAO S3 has 8 MB of PSRAM, this kit
runs the **full on-device renderer** - unlike the [TRMNL OG](/devices/trmnl-og), which is a thin
client.
Pick the **TRMNL 7.5" DIY Kit** hardware preset when you add the frame and the panel, GPIO map,
flash profile and the two buttons are set up for you.
## Setup [#setup]
1. In the FrameOS backend, create a frame with **Flash embedded device**.
2. Choose **ESP32-S3**, select the **TRMNL 7.5" DIY Kit** preset, and enter your WiFi details.
3. Flash it over USB from the browser - the backend writes the release image for the board and sets
it up as this frame.
4. Install scenes as usual - they sync over WiFi, and the device updates itself over the air.
Flashing FrameOS replaces the TRMNL firmware on the board. It does not touch, and does not need,
a TRMNL account or their servers - the frame talks to your own [backend](/guide/backend).
## Battery [#battery]
The XIAO ePaper driver board's battery connector is read through a 2:1 divider on GPIO 1, switched on by GPIO 6 - the preset carries the wiring (taken from the TRMNL firmware, not yet verified on this kit), so a cell shows its charge and the [deep sleep options](/guide/esp32#power) apply.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The 7.5" V2 panel driver is confirmed on
other ESP32-S3 boards. Tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) if you
try it.
# TRMNL OG (/devices/trmnl-og)
The **TRMNL OG** is the original 7.5" black/white TRMNL device, built on an **ESP32-C3**. The C3
has no PSRAM and about 380 KB of usable RAM, which is not enough for an on-device renderer -
so FrameOS runs it as a **thin client**: your [backend](/guide/backend) renders the scene and the
device streams the finished panel bytes straight into the e-paper controller.
That means one real trade-off compared to an [ESP32-S3 frame](/guide/esp32): the backend has to be
reachable when the frame renders. Everything else - scenes, apps, schedules, state fields - works
the same way.
## Setup [#setup]
1. In the FrameOS backend, create a frame with **Flash embedded device**.
2. Choose **ESP32-C3**, select the **TRMNL OG** preset, and enter your WiFi details.
3. Flash it over USB from the browser - the backend writes the release image for the board and sets
it up as this frame.
4. Install scenes as usual.
Flashing FrameOS replaces the TRMNL firmware, and needs no TRMNL account or servers. The C3 image
uses a 4 MB single-slot layout with no OTA partition, so firmware updates go over USB.
## Battery [#battery]
The TRMNL's cell is read through a 2:1 divider on GPIO 3; the preset carries the wiring (from the TRMNL firmware, not yet verified under FrameOS), so its charge shows in the backend's metrics.
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.**
Tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) if you try it.
# Waveshare 2.13" 250×122 e-Paper (/devices/waveshare-2.13inch-250x122-epaper)
* Device: [Waveshare 2.13" e-Paper HAT](https://www.waveshare.com/2.13inch-Touch-e-Paper-HAT.htm?\&aff_id=79380)
Several hardware revisions exist (V2, V3, V4, plus 3-color and 4-color "G" variants) - they all
have drivers in FrameOS. If you're not sure which revision you have, try the V4 driver first and
work backwards until the image looks right.
# Waveshare 5" round 1080×1080 LCD (/devices/waveshare-5inch-round-1080x1080-lcd)
* Device: [Waveshare 5" round 1080×1080 LCD](https://www.waveshare.com/5inch-1080x1080-lcd.htm?\&aff_id=79380)
([wiki](https://www.waveshare.com/wiki/5inch_1080x1080_LCD))
* Driver: [HDMI / Framebuffer](/devices/framebuffer-hdmi)
This display connects over HDMI, so it uses the framebuffer driver. It needs custom HDMI timings:
add the following to the end of `/boot/config.txt` and reboot:
```ini
hdmi_group=2
hdmi_mode=87
hdmi_pixel_freq_limit=356000000
hdmi_timings=1080 0 68 32 100 1080 0 12 4 16 0 0 0 60 0 85500000 0
```
## Power control [#power-control]
Enable the Fake KMS driver:
```bash
sudo sed -i '/dtoverlay=vc4-kms-v3d/s/kms/fkms/' /boot/config.txt
```
Then toggle the display from the command line:
```bash
vcgencmd display_power 0 # off
vcgencmd display_power 1 # on
vcgencmd display_power | grep "display_power=0" >/dev/null && echo "off" || echo "on"
```
# Waveshare 7.5" 800×480 e-Paper (/devices/waveshare-7.5inch-800x480-epaper)
* Device: [Waveshare 7.5" e-Paper HAT](https://www.waveshare.com/7.5inch-e-Paper-HAT.htm?\&aff_id=79380)
* Variants: black/white (V2), black/white/red (B V2)
A popular, affordable starter panel. The HAT plugs directly onto the Pi's GPIO header - or connect
the included adapter board with the supplied cable.
## Setup [#setup]
1. Connect the panel to the Pi.
2. Select the matching **Waveshare 7.5"** driver variant when adding the frame (V2 for current
black/white panels, B V2 for the 3-color version).
3. Save & deploy. The display will blink through its refresh cycle and show your scene.
If the image looks wrong, try the other 7.5" driver variants - Waveshare has shipped several
revisions under the same product name.
## ESP32 support [#esp32-support]
This panel is supported by the [FrameOS ESP32 firmware](/guide/esp32): wire it to an ESP32-S3 over
SPI, flash from the browser, and scenes render on the microcontroller itself - with over-the-air
updates and an optional deep-sleep battery mode. Pick the exact Waveshare 7.5" driver variant when
building firmware; each ESP32 image contains one selected panel driver.
# Waveshare 10.2" (B) e-Paper (/devices/waveshare-epd-10in2b)
The **Waveshare 10.2" (B) e-Paper** is a 10.2″ e-ink panel with a resolution of
**960×640 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_10in2b`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 10.2" (B) 960x640 Black/White/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 10.3" e-Paper (/devices/waveshare-epd-10in3)
The **Waveshare 10.3" e-Paper** is a 10.3″ e-ink panel with a resolution of
**1872×1404 pixels** showing **16-level grayscale**. Grayscale e-ink renders dithered photos noticeably better than pure black-and-white panels, while keeping a quick refresh.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
This panel is driven through the IT8951 controller board (USB/SPI). It works, but draws more power than typical SPI panels. From FrameOS 2026.9.18 a full refresh takes about 4 seconds on a Pi Zero 2 W - it used to be closer to 50.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_10in3`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 10.3" 1872x1404 16 Grayscale** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟢 **Confirmed working** - tested on real hardware by the maintainers or the community.
# Waveshare 12.48" e-Paper (/devices/waveshare-epd-12in48)
The **Waveshare 12.48" e-Paper** is a 12.48″ e-ink panel with a resolution of
**1304×984 pixels** showing **black and white**. Two-tone e-ink has the fastest refresh of any e-paper type - a full update takes only a couple of seconds - and the high contrast works great for text, dashboards and dithered photos.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
The 12.48" panel uses two driver boards and its own wiring - follow [the 12.48" build video](/blog/frameos-2024) for assembly.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_12in48`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 12.48" 1304x984 Black/White** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 12.48" (B V2) e-Paper (/devices/waveshare-epd-12in48b-v2)
The **Waveshare 12.48" (B V2) e-Paper** is a 12.48″ e-ink panel with a resolution of
**1304×984 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
The 12.48" panel uses two driver boards and its own wiring - follow [the 12.48" build video](/blog/frameos-2024) for assembly.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_12in48b_V2`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 12.48" (B V2) 1304x984 Black/White/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟢 **Confirmed working** - tested on real hardware by the maintainers or the community.
# Waveshare 12.48" (B) e-Paper (/devices/waveshare-epd-12in48b)
The **Waveshare 12.48" (B) e-Paper** is a 12.48″ e-ink panel with a resolution of
**1304×984 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
The 12.48" panel uses two driver boards and its own wiring - follow [the 12.48" build video](/blog/frameos-2024) for assembly.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_12in48b`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 12.48" (B) 1304x984 Black/White/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟢 **Confirmed working** - tested on real hardware by the maintainers or the community.
# Waveshare 13.3" (B) e-Paper (/devices/waveshare-epd-13in3b)
The **Waveshare 13.3" (B) e-Paper** is a 13.3″ e-ink panel with a resolution of
**960×680 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_13in3b`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 13.3" (B) 960x680 Black/White/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 13.3" (E) e-Paper (/devices/waveshare-epd-13in3e)
The **Waveshare 13.3" (E) e-Paper** is a 13.3″ e-ink panel with a resolution of
**1600×1200 pixels** showing **six Spectra 6 ink colors**. Spectra 6 is the current generation of color e-ink: six vivid ink colors with much better saturation than the older 7-color ACeP panels. A full refresh takes roughly 15-30 seconds.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
See the [Spectra 6 build video](/blog/eink-spectra-waveshare-pimoroni) for a side-by-side look at color e-ink generations.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_13in3e`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 13.3" (E) 1600x1200 Spectra 6 Color** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## ESP32 support [#esp32-support]
This panel also has a dedicated ESP32-S3 path. For Waveshare's all-in-one ESP32-S3 13.3E6
hardware, use the [Waveshare ESP32-S3 ePaper 13.3E6](/devices/waveshare-esp32-s3-epaper-13in3e6)
preset page.
## Status [#status]
🟢 **Confirmed working** - tested on real hardware by the maintainers or the community.
# Waveshare 13.3" (K) e-Paper (/devices/waveshare-epd-13in3k)
The **Waveshare 13.3" (K) e-Paper** is a 13.3″ e-ink panel with a resolution of
**960×680 pixels** showing **black and white**. Two-tone e-ink has the fastest refresh of any e-paper type - a full update takes only a couple of seconds - and the high contrast works great for text, dashboards and dithered photos.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_13in3k`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 13.3" (K) 960x680 Black/White** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 1.02" (D) e-Paper (/devices/waveshare-epd-1in02d)
The **Waveshare 1.02" (D) e-Paper** is a 1.02″ e-ink panel with a resolution of
**128×80 pixels** showing **black and white**. Two-tone e-ink has the fastest refresh of any e-paper type - a full update takes only a couple of seconds - and the high contrast works great for text, dashboards and dithered photos.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_1in02d`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 1.02" (D) 128x80 Black/White** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 1.54" (DES) e-Paper (/devices/waveshare-epd-1in54-des)
The **Waveshare 1.54" (DES) e-Paper** is a 1.54″ e-ink panel with a resolution of
**152×152 pixels** showing **black and white**. Two-tone e-ink has the fastest refresh of any e-paper type - a full update takes only a couple of seconds - and the high contrast works great for text, dashboards and dithered photos.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_1in54_DES`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 1.54" (DES) 152x152 Black/White** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 1.54" (V2) e-Paper (/devices/waveshare-epd-1in54-v2)
The **Waveshare 1.54" (V2) e-Paper** is a 1.54″ e-ink panel with a resolution of
**200×200 pixels** showing **black and white**. Two-tone e-ink has the fastest refresh of any e-paper type - a full update takes only a couple of seconds - and the high contrast works great for text, dashboards and dithered photos.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_1in54_V2`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 1.54" (V2) 200x200 Black/White** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 1.54" e-Paper (/devices/waveshare-epd-1in54)
The **Waveshare 1.54" e-Paper** is a 1.54″ e-ink panel with a resolution of
**200×200 pixels** showing **black and white**. Two-tone e-ink has the fastest refresh of any e-paper type - a full update takes only a couple of seconds - and the high contrast works great for text, dashboards and dithered photos.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_1in54`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 1.54" 200x200 Black/White** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 1.54" (B V2) e-Paper (/devices/waveshare-epd-1in54b-v2)
The **Waveshare 1.54" (B V2) e-Paper** is a 1.54″ e-ink panel with a resolution of
**200×200 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_1in54b_V2`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 1.54" (B V2) 200x200 Black/White/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 1.54" (B) e-Paper (/devices/waveshare-epd-1in54b)
The **Waveshare 1.54" (B) e-Paper** is a 1.54″ e-ink panel with a resolution of
**200×200 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_1in54b`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 1.54" (B) 200x200 Black/White/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 1.54" (C) e-Paper (/devices/waveshare-epd-1in54c)
The **Waveshare 1.54" (C) e-Paper** is a 1.54″ e-ink panel with a resolution of
**152×152 pixels** showing **black, white and yellow**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_1in54c`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 1.54" (C) 152x152 Black/White/Yellow** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 1.64" (G) e-Paper (/devices/waveshare-epd-1in64g)
The **Waveshare 1.64" (G) e-Paper** is a 1.64″ e-ink panel with a resolution of
**168×168 pixels** showing **black, white, yellow and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_1in64g`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 1.64" (G) 168x168 Black/White/Yellow/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.13" (DES) e-Paper (/devices/waveshare-epd-2in13-des)
The **Waveshare 2.13" (DES) e-Paper** is a 2.13″ e-ink panel with a resolution of
**212×104 pixels** showing **black and white**. Two-tone e-ink has the fastest refresh of any e-paper type - a full update takes only a couple of seconds - and the high contrast works great for text, dashboards and dithered photos.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in13_DES`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.13" (DES) 212x104 Black/White** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.13" (V2) e-Paper (/devices/waveshare-epd-2in13-v2)
The **Waveshare 2.13" (V2) e-Paper** is a 2.13″ e-ink panel with a resolution of
**250×122 pixels** showing **black and white**. Two-tone e-ink has the fastest refresh of any e-paper type - a full update takes only a couple of seconds - and the high contrast works great for text, dashboards and dithered photos.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in13_V2`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.13" (V2) 250x122 Black/White** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.13" (V3) e-Paper (/devices/waveshare-epd-2in13-v3)
The **Waveshare 2.13" (V3) e-Paper** is a 2.13″ e-ink panel with a resolution of
**250×122 pixels** showing **black and white**. Two-tone e-ink has the fastest refresh of any e-paper type - a full update takes only a couple of seconds - and the high contrast works great for text, dashboards and dithered photos.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in13_V3`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.13" (V3) 250x122 Black/White** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.13" e-Paper (/devices/waveshare-epd-2in13)
The **Waveshare 2.13" e-Paper** is a 2.13″ e-ink panel with a resolution of
**250×122 pixels** showing **black and white**. Two-tone e-ink has the fastest refresh of any e-paper type - a full update takes only a couple of seconds - and the high contrast works great for text, dashboards and dithered photos.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in13`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.13" 250x122 Black/White** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟢 **Confirmed working** - tested on real hardware by the maintainers or the community.
# Waveshare 2.13" (B V3) e-Paper (/devices/waveshare-epd-2in13b-v3)
The **Waveshare 2.13" (B V3) e-Paper** is a 2.13″ e-ink panel with a resolution of
**212×104 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in13b_V3`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.13" (B V3) 212x104 Black/White/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.13" (B V4) e-Paper (/devices/waveshare-epd-2in13b-v4)
The **Waveshare 2.13" (B V4) e-Paper** is a 2.13″ e-ink panel with a resolution of
**250×122 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in13b_V4`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.13" (B V4) 250x122 Black/White/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.13" (B) e-Paper (/devices/waveshare-epd-2in13b)
The **Waveshare 2.13" (B) e-Paper** is a 2.13″ e-ink panel with a resolution of
**212×104 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in13b`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.13" (B) 212x104 Black/White/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.13" (BC) e-Paper (/devices/waveshare-epd-2in13bc)
The **Waveshare 2.13" (BC) e-Paper** is a 2.13″ e-ink panel with a resolution of
**212×104 pixels** showing **black, white and yellow**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in13bc`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.13" (BC) 212x104 Black/White/Yellow** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.13" (C) e-Paper (/devices/waveshare-epd-2in13c)
The **Waveshare 2.13" (C) e-Paper** is a 2.13″ e-ink panel with a resolution of
**212×104 pixels** showing **black, white and yellow**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in13c`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.13" (C) 212x104 Black/White/Yellow** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.13" (D) e-Paper (/devices/waveshare-epd-2in13d)
The **Waveshare 2.13" (D) e-Paper** is a 2.13″ e-ink panel with a resolution of
**212×104 pixels** showing **black and white**. Two-tone e-ink has the fastest refresh of any e-paper type - a full update takes only a couple of seconds - and the high contrast works great for text, dashboards and dithered photos.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in13d`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.13" (D) 212x104 Black/White** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.13" (G V2) e-Paper (/devices/waveshare-epd-2in13g-v2)
The **Waveshare 2.13" (G V2) e-Paper** is a 2.13″ e-ink panel with a resolution of
**250×122 pixels** showing **black, white, yellow and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in13g_V2`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.13" (G V2) 250x122 Black/White/Yellow/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.13" (G) e-Paper (/devices/waveshare-epd-2in13g)
The **Waveshare 2.13" (G) e-Paper** is a 2.13″ e-ink panel with a resolution of
**250×122 pixels** showing **black, white, yellow and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in13g`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.13" (G) 250x122 Black/White/Yellow/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.15" (B) e-Paper (/devices/waveshare-epd-2in15b)
The **Waveshare 2.15" (B) e-Paper** is a 2.15″ e-ink panel with a resolution of
**296×160 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in15b`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.15" (B) 296x160 Black/White/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.15" (G) e-Paper (/devices/waveshare-epd-2in15g)
The **Waveshare 2.15" (G) e-Paper** is a 2.15″ e-ink panel with a resolution of
**296×160 pixels** showing **black, white, yellow and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in15g`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.15" (G) 296x160 Black/White/Yellow/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.36" (G) e-Paper (/devices/waveshare-epd-2in36g)
The **Waveshare 2.36" (G) e-Paper** is a 2.36″ e-ink panel with a resolution of
**296×168 pixels** showing **black, white, yellow and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in36g`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.36" (G) 296x168 Black/White/Yellow/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.66" e-Paper (/devices/waveshare-epd-2in66)
The **Waveshare 2.66" e-Paper** is a 2.66″ e-ink panel with a resolution of
**296×152 pixels** showing **black and white**. Two-tone e-ink has the fastest refresh of any e-paper type - a full update takes only a couple of seconds - and the high contrast works great for text, dashboards and dithered photos.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in66`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.66" 296x152 Black/White** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.66" (B) e-Paper (/devices/waveshare-epd-2in66b)
The **Waveshare 2.66" (B) e-Paper** is a 2.66″ e-ink panel with a resolution of
**296×152 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in66b`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.66" (B) 296x152 Black/White/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.66" (G) e-Paper (/devices/waveshare-epd-2in66g)
The **Waveshare 2.66" (G) e-Paper** is a 2.66″ e-ink panel with a resolution of
**360×184 pixels** showing **black, white, yellow and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in66g`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.66" (G) 360x184 Black/White/Yellow/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.7" (V2) e-Paper (/devices/waveshare-epd-2in7-v2)
The **Waveshare 2.7" (V2) e-Paper** is a 2.7″ e-ink panel with a resolution of
**264×176 pixels** showing **4-level grayscale**. Grayscale e-ink renders dithered photos noticeably better than pure black-and-white panels, while keeping a quick refresh.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in7_V2`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.7" (V2) 264x176 4 Grayscale** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.7" e-Paper (/devices/waveshare-epd-2in7)
The **Waveshare 2.7" e-Paper** is a 2.7″ e-ink panel with a resolution of
**264×176 pixels** showing **4-level grayscale**. Grayscale e-ink renders dithered photos noticeably better than pure black-and-white panels, while keeping a quick refresh.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in7`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.7" 264x176 4 Grayscale** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.7" (B V2) e-Paper (/devices/waveshare-epd-2in7b-v2)
The **Waveshare 2.7" (B V2) e-Paper** is a 2.7″ e-ink panel with a resolution of
**264×176 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in7b_V2`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.7" (B V2) 264x176 Black/White/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.7" (B) e-Paper (/devices/waveshare-epd-2in7b)
The **Waveshare 2.7" (B) e-Paper** is a 2.7″ e-ink panel with a resolution of
**264×176 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in7b`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.7" (B) 264x176 Black/White/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.9" (DES) e-Paper (/devices/waveshare-epd-2in9-des)
The **Waveshare 2.9" (DES) e-Paper** is a 2.9″ e-ink panel with a resolution of
**296×128 pixels** showing **black and white**. Two-tone e-ink has the fastest refresh of any e-paper type - a full update takes only a couple of seconds - and the high contrast works great for text, dashboards and dithered photos.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in9_DES`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.9" (DES) 296x128 Black/White** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.9" (V2) e-Paper (/devices/waveshare-epd-2in9-v2)
The **Waveshare 2.9" (V2) e-Paper** is a 2.9″ e-ink panel with a resolution of
**296×128 pixels** showing **4-level grayscale**. Grayscale e-ink renders dithered photos noticeably better than pure black-and-white panels, while keeping a quick refresh.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in9_V2`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.9" (V2) 296x128 4 Grayscale** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.9" (V3) e-Paper (/devices/waveshare-epd-2in9-v3)
The **Waveshare 2.9" (V3) e-Paper** is a 2.9″ e-ink panel with a resolution of
**296×128 pixels** showing **4-level grayscale**. Grayscale e-ink renders dithered photos noticeably better than pure black-and-white panels, while keeping a quick refresh.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in9_V3`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.9" (V3) 296x128 4 Grayscale** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
This is the panel revision Waveshare started shipping in 2026; FrameOS 2026.8.40 added its driver, alongside the [V2](/devices/waveshare-epd-2in9-v2). Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.9" e-Paper (/devices/waveshare-epd-2in9)
The **Waveshare 2.9" e-Paper** is a 2.9″ e-ink panel with a resolution of
**296×128 pixels** showing **black and white**. Two-tone e-ink has the fastest refresh of any e-paper type - a full update takes only a couple of seconds - and the high contrast works great for text, dashboards and dithered photos.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in9`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.9" 296x128 Black/White** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.9" (B V3) e-Paper (/devices/waveshare-epd-2in9b-v3)
The **Waveshare 2.9" (B V3) e-Paper** is a 2.9″ e-ink panel with a resolution of
**296×128 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in9b_V3`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.9" (B V3) 296x128 Black/White/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.9" (B V4) e-Paper (/devices/waveshare-epd-2in9b-v4)
The **Waveshare 2.9" (B V4) e-Paper** is a 2.9″ e-ink panel with a resolution of
**296×128 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in9b_V4`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.9" (B V4) 296x128 Black/White/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.9" (B) e-Paper (/devices/waveshare-epd-2in9b)
The **Waveshare 2.9" (B) e-Paper** is a 2.9″ e-ink panel with a resolution of
**296×128 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in9b`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.9" (B) 296x128 Black/White/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.9" (BC) e-Paper (/devices/waveshare-epd-2in9bc)
The **Waveshare 2.9" (BC) e-Paper** is a 2.9″ e-ink panel with a resolution of
**296×128 pixels** showing **black, white and yellow**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in9bc`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.9" (BC) 296x128 Black/White/Yellow** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.9" (C) e-Paper (/devices/waveshare-epd-2in9c)
The **Waveshare 2.9" (C) e-Paper** is a 2.9″ e-ink panel with a resolution of
**296×128 pixels** showing **black, white and yellow**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in9c`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.9" (C) 296x128 Black/White/Yellow** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 2.9" (D) e-Paper (/devices/waveshare-epd-2in9d)
The **Waveshare 2.9" (D) e-Paper** is a 2.9″ e-ink panel with a resolution of
**296×128 pixels** showing **black and white**. Two-tone e-ink has the fastest refresh of any e-paper type - a full update takes only a couple of seconds - and the high contrast works great for text, dashboards and dithered photos.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_2in9d`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 2.9" (D) 296x128 Black/White** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 3" (G) e-Paper (/devices/waveshare-epd-3in0g)
The **Waveshare 3" (G) e-Paper** is a 3″ e-ink panel with a resolution of
**400×168 pixels** showing **black, white, yellow and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_3in0g`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 3" (G) 400x168 Black/White/Yellow/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 3.52" e-Paper (/devices/waveshare-epd-3in52)
The **Waveshare 3.52" e-Paper** is a 3.52″ e-ink panel with a resolution of
**360×240 pixels** showing **black and white**. Two-tone e-ink has the fastest refresh of any e-paper type - a full update takes only a couple of seconds - and the high contrast works great for text, dashboards and dithered photos.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_3in52`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 3.52" 360x240 Black/White** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 3.52" (B) e-Paper (/devices/waveshare-epd-3in52b)
The **Waveshare 3.52" (B) e-Paper** is a 3.52″ e-ink panel with a resolution of
**360×240 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_3in52b`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 3.52" (B) 360x240 Black/White/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 3.7" e-Paper (/devices/waveshare-epd-3in7)
The **Waveshare 3.7" e-Paper** is a 3.7″ e-ink panel with a resolution of
**480×280 pixels** showing **4-level grayscale**. Grayscale e-ink renders dithered photos noticeably better than pure black-and-white panels, while keeping a quick refresh.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_3in7`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 3.7" 480x280 4 Grayscale** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 4.01" (F) e-Paper (/devices/waveshare-epd-4in01f)
The **Waveshare 4.01" (F) e-Paper** is a 4.01″ e-ink panel with a resolution of
**640×400 pixels** showing **seven ACeP ink colors**. ACeP ("Advanced Color ePaper") packs seven real ink colors into every pixel. A full refresh takes around 30 seconds, and the palette is softer than a backlit screen - ideal for photos and generative art with a paper-like look.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
See the [Spectra 6 build video](/blog/eink-spectra-waveshare-pimoroni) for a side-by-side look at color e-ink generations.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_4in01f`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 4.01" (F) 640x400 7 Color** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟢 **Confirmed working** - tested on real hardware by the maintainers or the community.
# Waveshare 4" (E) e-Paper (/devices/waveshare-epd-4in0e)
The **Waveshare 4" (E) e-Paper** is a 4″ e-ink panel with a resolution of
**600×400 pixels** showing **six Spectra 6 ink colors**. Spectra 6 is the current generation of color e-ink: six vivid ink colors with much better saturation than the older 7-color ACeP panels. A full refresh takes roughly 15-30 seconds.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
See the [Spectra 6 build video](/blog/eink-spectra-waveshare-pimoroni) for a side-by-side look at color e-ink generations.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_4in0e`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 4" (E) 600x400 Spectra 6 Color** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟢 **Confirmed working** - tested on real hardware by the maintainers or the community.
# Waveshare 4.2" (V2) e-Paper (/devices/waveshare-epd-4in2-v2)
The **Waveshare 4.2" (V2) e-Paper** is a 4.2″ e-ink panel with a resolution of
**400×300 pixels** showing **4-level grayscale**. Grayscale e-ink renders dithered photos noticeably better than pure black-and-white panels, while keeping a quick refresh.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_4in2_V2`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 4.2" (V2) 400x300 4 Grayscale** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 4.2" e-Paper (/devices/waveshare-epd-4in2)
The **Waveshare 4.2" e-Paper** is a 4.2″ e-ink panel with a resolution of
**400×300 pixels** showing **4-level grayscale**. Grayscale e-ink renders dithered photos noticeably better than pure black-and-white panels, while keeping a quick refresh.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_4in2`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 4.2" 400x300 4 Grayscale** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 4.26" e-Paper (/devices/waveshare-epd-4in26)
The **Waveshare 4.26" e-Paper** is a 4.26″ e-ink panel with a resolution of
**800×480 pixels** showing **4-level grayscale**. Grayscale e-ink renders dithered photos noticeably better than pure black-and-white panels, while keeping a quick refresh.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_4in26`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 4.26" 800x480 4 Grayscale** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 4.2" (B V2 OLD) e-Paper (/devices/waveshare-epd-4in2b-v2-old)
The **Waveshare 4.2" (B V2 OLD) e-Paper** is a 4.2″ e-ink panel with a resolution of
**400×300 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_4in2b_V2_old`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 4.2" (B V2 OLD) 400x300 Black/White/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 4.2" (B V2) e-Paper (/devices/waveshare-epd-4in2b-v2)
The **Waveshare 4.2" (B V2) e-Paper** is a 4.2″ e-ink panel with a resolution of
**400×300 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_4in2b_V2`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 4.2" (B V2) 400x300 Black/White/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 4.2" (B) e-Paper (/devices/waveshare-epd-4in2b)
The **Waveshare 4.2" (B) e-Paper** is a 4.2″ e-ink panel with a resolution of
**400×300 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_4in2b`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 4.2" (B) 400x300 Black/White/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 4.2" (BC) e-Paper (/devices/waveshare-epd-4in2bc)
The **Waveshare 4.2" (BC) e-Paper** is a 4.2″ e-ink panel with a resolution of
**400×300 pixels** showing **black, white and yellow**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_4in2bc`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 4.2" (BC) 400x300 Black/White/Yellow** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 4.2" (C) e-Paper (/devices/waveshare-epd-4in2c)
The **Waveshare 4.2" (C) e-Paper** is a 4.2″ e-ink panel with a resolution of
**400×300 pixels** showing **black, white and yellow**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_4in2c`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 4.2" (C) 400x300 Black/White/Yellow** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 4.37" (B) e-Paper (/devices/waveshare-epd-4in37b)
The **Waveshare 4.37" (B) e-Paper** is a 4.37″ e-ink panel with a resolution of
**480×176 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_4in37b`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 4.37" (B) 480x176 Black/White/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 4.37" (G) e-Paper (/devices/waveshare-epd-4in37g)
The **Waveshare 4.37" (G) e-Paper** is a 4.37″ e-ink panel with a resolution of
**512×368 pixels** showing **black, white, yellow and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_4in37g`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 4.37" (G) 512x368 Black/White/Yellow/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 5.65" (F) e-Paper (/devices/waveshare-epd-5in65f)
The **Waveshare 5.65" (F) e-Paper** is a 5.65″ e-ink panel with a resolution of
**600×448 pixels** showing **seven ACeP ink colors**. ACeP ("Advanced Color ePaper") packs seven real ink colors into every pixel. A full refresh takes around 30 seconds, and the palette is softer than a backlit screen - ideal for photos and generative art with a paper-like look.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
See the [Spectra 6 build video](/blog/eink-spectra-waveshare-pimoroni) for a side-by-side look at color e-ink generations.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_5in65f`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 5.65" (F) 600x448 7 Color** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 5.79" e-Paper (/devices/waveshare-epd-5in79)
The **Waveshare 5.79" e-Paper** is a 5.79″ e-ink panel with a resolution of
**792×272 pixels** showing **4-level grayscale**. Grayscale e-ink renders dithered photos noticeably better than pure black-and-white panels, while keeping a quick refresh.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_5in79`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 5.79" 792x272 4 Grayscale** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 5.79" (B) e-Paper (/devices/waveshare-epd-5in79b)
The **Waveshare 5.79" (B) e-Paper** is a 5.79″ e-ink panel with a resolution of
**792×272 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_5in79b`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 5.79" (B) 792x272 Black/White/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 5.79" (G) e-Paper (/devices/waveshare-epd-5in79g)
The **Waveshare 5.79" (G) e-Paper** is a 5.79″ e-ink panel with a resolution of
**792×272 pixels** showing **black, white, yellow and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_5in79g`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 5.79" (G) 792x272 Black/White/Yellow/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 5.83" (V2) e-Paper (/devices/waveshare-epd-5in83-v2)
The **Waveshare 5.83" (V2) e-Paper** is a 5.83″ e-ink panel with a resolution of
**648×480 pixels** showing **4-level grayscale**. Grayscale e-ink renders dithered photos noticeably better than pure black-and-white panels, while keeping a quick refresh.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_5in83_V2`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 5.83" (V2) 648x480 4 Grayscale** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 5.83" e-Paper (/devices/waveshare-epd-5in83)
The **Waveshare 5.83" e-Paper** is a 5.83″ e-ink panel with a resolution of
**600×448 pixels** showing **black and white**. Two-tone e-ink has the fastest refresh of any e-paper type - a full update takes only a couple of seconds - and the high contrast works great for text, dashboards and dithered photos.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_5in83`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 5.83" 600x448 Black/White** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 5.83" (B V2) e-Paper (/devices/waveshare-epd-5in83b-v2)
The **Waveshare 5.83" (B V2) e-Paper** is a 5.83″ e-ink panel with a resolution of
**648×480 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_5in83b_V2`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 5.83" (B V2) 648x480 Black/White/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 5.83" (B) e-Paper (/devices/waveshare-epd-5in83b)
The **Waveshare 5.83" (B) e-Paper** is a 5.83″ e-ink panel with a resolution of
**600×448 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_5in83b`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 5.83" (B) 600x448 Black/White/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 5.83" (BC) e-Paper (/devices/waveshare-epd-5in83bc)
The **Waveshare 5.83" (BC) e-Paper** is a 5.83″ e-ink panel with a resolution of
**600×448 pixels** showing **black, white and yellow**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_5in83bc`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 5.83" (BC) 600x448 Black/White/Yellow** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 5.83" (C) e-Paper (/devices/waveshare-epd-5in83c)
The **Waveshare 5.83" (C) e-Paper** is a 5.83″ e-ink panel with a resolution of
**600×448 pixels** showing **black, white and yellow**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_5in83c`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 5.83" (C) 600x448 Black/White/Yellow** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 5.84" e-Paper (/devices/waveshare-epd-5in84)
The **Waveshare 5.84" e-Paper** is a 5.84″ e-ink panel with a resolution of
**768×256 pixels** showing **black and white**. Two-tone e-ink has the fastest refresh of any e-paper type - a full update takes only a couple of seconds - and the high contrast works great for text, dashboards and dithered photos.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_5in84`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 5.84" 768x256 Black/White** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 7.3" (E) e-Paper (/devices/waveshare-epd-7in3e)
The **Waveshare 7.3" (E) e-Paper** is a 7.3″ e-ink panel with a resolution of
**800×480 pixels** showing **six Spectra 6 ink colors**. Spectra 6 is the current generation of color e-ink: six vivid ink colors with much better saturation than the older 7-color ACeP panels. A full refresh takes roughly 15-30 seconds.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
See the [Spectra 6 build video](/blog/eink-spectra-waveshare-pimoroni) for a side-by-side look at color e-ink generations.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_7in3e`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 7.3" (E) 800x480 Spectra 6 Color** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟢 **Confirmed working** - tested on real hardware by the maintainers or the community.
# Waveshare 7.3" (F) e-Paper (/devices/waveshare-epd-7in3f)
The **Waveshare 7.3" (F) e-Paper** is a 7.3″ e-ink panel with a resolution of
**800×480 pixels** showing **seven ACeP ink colors**. ACeP ("Advanced Color ePaper") packs seven real ink colors into every pixel. A full refresh takes around 30 seconds, and the palette is softer than a backlit screen - ideal for photos and generative art with a paper-like look.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
See the [Spectra 6 build video](/blog/eink-spectra-waveshare-pimoroni) for a side-by-side look at color e-ink generations.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_7in3f`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 7.3" (F) 800x480 7 Color** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 7.3" (G) e-Paper (/devices/waveshare-epd-7in3g)
The **Waveshare 7.3" (G) e-Paper** is a 7.3″ e-ink panel with a resolution of
**800×480 pixels** showing **black, white, yellow and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_7in3g`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 7.3" (G) 800x480 Black/White/Yellow/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 7.5" (HD) e-Paper (/devices/waveshare-epd-7in5-hd)
The **Waveshare 7.5" (HD) e-Paper** is a 7.5″ e-ink panel with a resolution of
**880×528 pixels** showing **black and white**. Two-tone e-ink has the fastest refresh of any e-paper type - a full update takes only a couple of seconds - and the high contrast works great for text, dashboards and dithered photos.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_7in5_HD`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 7.5" (HD) 880x528 Black/White** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 7.5" (V2 GRAY) e-Paper (/devices/waveshare-epd-7in5-v2-gray)
The **Waveshare 7.5" (V2 GRAY) e-Paper** is a 7.5″ e-ink panel with a resolution of
**800×480 pixels** showing **4-level grayscale**. Grayscale e-ink renders dithered photos noticeably better than pure black-and-white panels, while keeping a quick refresh.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_7in5_V2_gray`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 7.5" (V2 GRAY) 800x480 4 Grayscale** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 7.5" e-Paper (/devices/waveshare-epd-7in5)
The **Waveshare 7.5" e-Paper** is a 7.5″ e-ink panel with a resolution of
**640×384 pixels** showing **black and white**. Two-tone e-ink has the fastest refresh of any e-paper type - a full update takes only a couple of seconds - and the high contrast works great for text, dashboards and dithered photos.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_7in5`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 7.5" 640x384 Black/White** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 7.5" (B HD) e-Paper (/devices/waveshare-epd-7in5b-hd)
The **Waveshare 7.5" (B HD) e-Paper** is a 7.5″ e-ink panel with a resolution of
**880×528 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_7in5b_HD`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 7.5" (B HD) 880x528 Black/White/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 7.5" (B V2 OLD) e-Paper (/devices/waveshare-epd-7in5b-v2-old)
The **Waveshare 7.5" (B V2 OLD) e-Paper** is a 7.5″ e-ink panel with a resolution of
**800×480 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_7in5b_V2_old`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 7.5" (B V2 OLD) 800x480 Black/White/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 7.5" (B V2) e-Paper (/devices/waveshare-epd-7in5b-v2)
The **Waveshare 7.5" (B V2) e-Paper** is a 7.5″ e-ink panel with a resolution of
**800×480 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_7in5b_V2`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 7.5" (B V2) 800x480 Black/White/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟢 **Confirmed working** - tested on real hardware by the maintainers or the community.
# Waveshare 7.5" (B) e-Paper (/devices/waveshare-epd-7in5b)
The **Waveshare 7.5" (B) e-Paper** is a 7.5″ e-ink panel with a resolution of
**640×384 pixels** showing **black, white and red**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_7in5b`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 7.5" (B) 640x384 Black/White/Red** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 7.5" (BC) e-Paper (/devices/waveshare-epd-7in5bc)
The **Waveshare 7.5" (BC) e-Paper** is a 7.5″ e-ink panel with a resolution of
**640×384 pixels** showing **black, white and yellow**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_7in5bc`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 7.5" (BC) 640x384 Black/White/Yellow** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare 7.5" (C) e-Paper (/devices/waveshare-epd-7in5c)
The **Waveshare 7.5" (C) e-Paper** is a 7.5″ e-ink panel with a resolution of
**640×384 pixels** showing **black, white and yellow**. Panels with an accent ink run a multi-pass refresh that takes roughly 15-30 seconds, so they suit art, calendars and dashboards that update every few minutes rather than every second.
Like all e-paper, it keeps the last image with zero power - the frame only wakes up to render.
## How it works [#how-it-works]
FrameOS ships a [native driver](https://github.com/FrameOS/frameos/tree/main/frameos/src/drivers/waveshare)
(`waveshare.EPD_7in5c`) compiled straight into the binary that runs on your Raspberry Pi - no Python, no
vendor libraries. Each render pass draws your [scene](/guide/scenes) into a full-color image,
dithers it to the panel's palette, and pushes the result to the display over SPI.
## Setup [#setup]
1. Connect the panel to the Raspberry Pi - plug the HAT onto the GPIO header, or hook up the
separate driver board with the bundled ribbon cable.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 7.5" (C) 640x384 Black/White/Yellow** driver.
3. Save & deploy. The panel blinks through a refresh cycle and shows your scene.
Make sure the letter suffix on your panel's ribbon cable matches the driver - Waveshare reuses
product names across revisions. More tips in the
[Waveshare buying guide](/devices/waveshare-other-displays).
## Status [#status]
🟡 **Should work, not yet confirmed on real hardware.** The driver shares its controller logic
with panels that are confirmed working. If you have this display,
tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and we'll mark it confirmed.
# Waveshare ESP32-S3 ePaper 13.3E6 (/devices/waveshare-esp32-s3-epaper-13in3e6)
The **Waveshare ESP32-S3 ePaper 13.3E6** preset is for Waveshare's ESP32-S3 board paired with the
13.3" 1600×1200 Spectra 6 panel. In FrameOS it uses the hardware preset
`waveshare_esp32_s3_epaper_13_3e6`, which selects the `waveshare.EPD_13in3e` panel driver, the
13.3E6 GPIO map, and the TF card asset mount.
Use this page for the ESP32-S3 13.3E6 hardware. If you are driving the same panel from a
Raspberry Pi, use the [Waveshare 13.3" (E)](/devices/waveshare-epd-13in3e) page instead.
## How it works [#how-it-works]
FrameOS builds a dedicated ESP32-S3 firmware image for the board. The 13.3E6 preset uses 32MB
flash, 16MB PSRAM, dual chip-select wiring for the large panel, and `/srv/assets` on the TF card
for local assets. (The render canvas is 16-bit, so the same panel also renders on 8 MB boards
like the [Seeed reTerminal E1004](/devices/seeed-reterminal-e1004); this board's extra PSRAM is
headroom for big photos.)
The firmware renders [scenes](/guide/scenes) on-device, dithers them to the Spectra 6 palette,
and refreshes the panel over the board's SPI wiring.
## Setup [#setup]
1. In the FrameOS backend, create a frame with **Flash embedded device**.
2. Choose **ESP32-S3**, select the **Waveshare ESP32-S3 ePaper 13.3E6** hardware preset, and
enter WiFi details.
3. Flash it over USB from the browser - the backend writes the release image for the board and sets
it up as this frame.
4. Install scenes as usual. The device syncs scene JSON and renders without SSH or Linux.
## Pin map [#pin-map]
| Signal | GPIO |
| ------ | ---- |
| PWR | 1 |
| RST | 2 |
| CS2 | 3 |
| SCK | 9 |
| CS | 10 |
| DC | 11 |
| BUSY | 12 |
| MOSI | 46 |
## Status [#status]
🟢 **Confirmed working** - tested on real hardware by the maintainers or the community.
# Waveshare ESP32-S3 PhotoPainter (/devices/waveshare-esp32-s3-photopainter)
The **Waveshare ESP32-S3 PhotoPainter** is a standalone 7.3" Spectra 6 e-ink frame. In FrameOS it
uses the ESP32 hardware preset `waveshare_esp32_s3_photopainter`, which selects the
`waveshare.EPD_7in3e` panel driver, the PhotoPainter GPIO map, the BOOT and KEY1 buttons, and
the TF card asset mount.
Use this page for the all-in-one ESP32-S3 PhotoPainter board. If you are wiring the same 7.3"
panel to a generic ESP32-S3 board, use the [Waveshare 7.3" (E)](/devices/waveshare-epd-7in3e)
page and the [ESP32 guide](/guide/esp32) instead.
## How it works [#how-it-works]
FrameOS builds a dedicated ESP32-S3 firmware image for the board. The device renders
[scenes](/guide/scenes) on-device, dithers them to the Spectra 6 palette, then refreshes the
panel over the board's built-in SPI wiring. The PhotoPainter preset also enables the board-level
power handling needed by this hardware.
The preset uses 16MB flash, 8MB PSRAM, and mounts the TF card at `/srv/assets` for local assets.
## Setup [#setup]
1. In the FrameOS backend, create a frame with **Flash embedded device**.
2. Choose **ESP32-S3**, select the **Waveshare ESP32-S3 PhotoPainter** hardware preset, and enter
WiFi details.
3. Flash it over USB from the browser - the backend writes the release image for the board and sets
it up as this frame.
4. Install scenes as usual. The device syncs scene JSON and renders without SSH or Linux.
## Status [#status]
🟢 **Confirmed working** - tested on real hardware by the maintainers or the community.
# Waveshare buying guide (/devices/waveshare-other-displays)
FrameOS has native Nim drivers for nearly a hundred Waveshare e-Paper variants - see the
[device database](/devices). This page covers what's been verified on real hardware and a few
buying tips.
## Tested on real hardware [#tested-on-real-hardware]
* [Waveshare 13.3" (E)](/devices/waveshare-epd-13in3e) 1600×1200 **Spectra 6 color**
* [Waveshare 7.3" (E)](/devices/waveshare-epd-7in3e) 800×480 **Spectra 6 color**
* [Waveshare 4.0" (E)](/devices/waveshare-epd-4in0e) 600×400 **Spectra 6 color**
* [Waveshare 4.01" (F)](/devices/waveshare-epd-4in01f) 640×400 7-color
* [Waveshare 12.48" (B](/devices/waveshare-epd-12in48b) / [B V2)](/devices/waveshare-epd-12in48b-v2) 1304×984 black/white/red
* [Waveshare 7.5" (V2)](/devices/waveshare-7.5inch-800x480-epaper) 800×480 black/white and
[(B V2)](/devices/waveshare-epd-7in5b-v2) 3-color
* [Waveshare 2.13"](/devices/waveshare-2.13inch-250x122-epaper) 250×122 black/white
* [Waveshare 10.3"](/devices/waveshare-epd-10in3) 1872×1404 16-grayscale (IT8951 controller)
Everything else in the [device database](/devices) shares controller logic with these and should
work. If you confirm a panel, tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) and
we'll mark it tested.
## Buying tips [#buying-tips]
* **Spectra 6 (E) panels** are the current sweet spot for color e-ink: six real ink colors,
much better saturation than the older 7-color (F) ACeP panels. The
[build video](/blog/eink-spectra-waveshare-pimoroni) compares them side by side.
* Waveshare panels usually arrive as a **bare glass panel plus a separate driver HAT** connected
by a ribbon cable. That's more flexible for slim builds than Pimoroni's all-in-one HATs, but
slightly more fiddly. Handle the glass with care.
* Pay attention to the **letter suffix** (B, C, D, E, F, G, V2…) - it identifies the exact
revision and the right driver. The label is printed on the panel's ribbon cable.
* Driver picked but the image looks wrong? Try the neighbouring revisions of the same size -
Waveshare reuses product names across hardware generations.
## Known quirks [#known-quirks]
* The [12.48" panel](/devices/waveshare-epd-12in48b) uses two driver boards and its own wiring -
follow [the 12.48" build video](/blog/frameos-2024) for assembly.
* The [10.3" IT8951 panel](/devices/waveshare-epd-10in3) works but is grayscale-only and draws
more power than SPI panels.
# Waveshare RPi Zero PhotoPainter 7.3" (/devices/waveshare-rpi-zero-photopainter-7in3e)
The **Waveshare RPi Zero PhotoPainter 7.3"** profile drives the same 800×480 Spectra 6 panel as
the [Waveshare 7.3" (E)](/devices/waveshare-epd-7in3e) page, but with the GPIO defaults used by
the Raspberry Pi Zero PhotoPainter wiring.
Use this driver profile when the display is attached through the PhotoPainter Pi wiring. For the
standard Waveshare HAT or driver board, use `waveshare.EPD_7in3e` instead.
## How it works [#how-it-works]
FrameOS maps `waveshare.rpi_zero_photopainter_7in3e` to the native `EPD_7in3e` panel driver and
applies PhotoPainter-specific pin defaults:
| Signal | GPIO |
| ------ | ---- |
| RST | 17 |
| DC | 25 |
| CS | 8 |
| BUSY | 24 |
| SCLK | 11 |
| MOSI | 10 |
| PWR | 27 |
Each render pass draws your [scene](/guide/scenes), dithers it to the Spectra 6 palette, and
pushes it to the panel over SPI.
## Setup [#setup]
1. Install FrameOS on the Raspberry Pi Zero or Zero 2 W.
2. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the
**Waveshare 7.3" RPi Zero PhotoPainter - 800x480 Spectra 6 Color** driver.
3. Save & deploy. FrameOS uses the PhotoPainter pin map automatically.
## Status [#status]
🟢 **Confirmed working** - tested on real hardware by the maintainers or the community.
# Web only (no physical display) (/devices/web-only)
**Web only** runs a frame with no physical display attached. The frame renders your
[scenes](/guide/scenes) on schedule, and you view the result in any browser - straight from the
frame, or mirrored in the FrameOS backend.
Great for:
* Trying out FrameOS before any e-ink hardware arrives.
* Kiosk setups where another machine fullscreens the frame's URL.
* Debugging scenes without waiting for an e-ink refresh.
The [ESP32 firmware](/guide/esp32) supports this mode too ("No display / headless") - handy for
testing a board before the panel arrives.
## Setup [#setup]
1. [Add a frame](/guide/first-deploy) in the FrameOS backend and select the **Web only** driver.
2. Set any width and height you like - the canvas is fully RGB at whatever resolution you choose.
3. Save & deploy, then open the frame's URL in a browser.
## Status [#status]
🟢 **Confirmed working** - tested on real hardware by the maintainers or the community.
# XTEINK X4 (/devices/xteink-x4)
The **XTEINK X4** is a small 4.26" four-grayscale e-ink reader on an **ESP32-C3** with
16 MB of flash. Reflashed with FrameOS it becomes a pocket-sized frame - and 800×480 on a
4.26" panel is a lot of pixels per inch, so small text stays legible.
The C3 has no PSRAM, so FrameOS runs this board as a **thin client**: your
[backend](/guide/backend) renders the scene and the device streams the packed panel bytes. The
backend has to be reachable when the frame renders.
The X4's card slot shares its SPI bus with the e-paper controller, so SD-card assets are turned
off on this board.
The C3's \~380 KB of internal RAM is tight: the firmware reserves the 96 KB panel buffer
at boot, before WiFi fragments the heap, and `GET /status` reports the free heap and largest
block if you want to keep an eye on it. The backend dithers the four-grey bitmap, so gradients
and photos come out smooth rather than banded.
The X4 provisions from the 4 MB no-OTA generic image, so firmware updates go over USB; a
16 MB C3 release asset that would enable OTA is on the list.
## Setup [#setup]
1. In the FrameOS backend, create a frame with **Flash embedded device**.
2. Choose **ESP32-C3**, select the **XTEINK X4** preset, and enter your WiFi details.
3. Flash it over USB from the browser - the backend writes the release image for the board and sets
it up as this frame.
4. Install scenes as usual.
## Battery [#battery]
The X4's cell is read through a 2:1 divider on GPIO 0; the preset carries the wiring (from the TRMNL firmware's board table, not yet verified on this reader), so its charge shows in the backend's metrics.
## Status [#status]
🟡 **Boots and renders on real hardware**, but not yet widely reported.
Tell us on [GitHub](https://github.com/FrameOS/frameos/issues/new) or [Discord](https://discord.gg/9dT9y7EzUw) if you try it.
# FrameOS Remote (/guide/agent)
The on-device companion formerly called the **agent** is now called
[**FrameOS Remote**](/guide/remote).
This page is kept so old links still resolve. Use the
[FrameOS Remote guide](/guide/remote) for the current docs.
# Editing apps (/guide/apps)
Every blue and green node on a scene is an **app**: a small program with typed config fields, an
optional image output, and access to the scene's state. FrameOS ships with \~40 built-in apps, and
you can edit or write your own.
What's in the box, roughly:
* **Data**: clocks, iCal calendars and agendas, weather, Home Assistant sensors, waste-collection
schedules, JSON/XML parsing, HTTP downloads.
* **Images**: photo galleries, Unsplash, Wikimedia Commons, Immich, Google Photos, images from any
URL, files off the frame's own SD card, OpenAI image generation, RTSP camera snapshots, headless
Chromium screenshots.
* **Render**: text, QR codes, calendars, charts, SVG, gradients and colour fills, opacity, split
layouts, zoom/pan (Ken Burns), resize and rotate.
* **Logic**: if/else, set state, log, sleep control.
JavaScript apps can be written and edited in the [self-hosted backend](/guide/backend), in
[FrameOS Cloud](/guide/cloud) and on a [standalone frame's](/guide/standalone) admin page alike.
Custom **Nim** apps and Nim code nodes need a compiled scene, which only the self-hosted backend
can build - the cloud never compiles code. That path is legacy:
[convert such scenes to JavaScript](/guide/scenes#execution) where you can.
## Edit any app [#edit-any-app]
Click the **edit** button next to any app on a scene to open its source. JavaScript apps are
editable everywhere, the [scene store's](/guide/scene-store) editor included - saved changes are
forked onto that scene, leaving the original untouched.
The built-in apps are compiled Nim, so in an [interpreted scene](/guide/scenes#execution) their
source opens **read only**, with an **Open in GitHub** link; to customize one, write a JavaScript
app or an inline code node instead. In a legacy compiled scene on a Raspberry Pi, a scene-local Nim
app still opens for editing, under a notice offering to convert the scene to interpreted.
Apps run with full access on the frame. Read the source before installing scenes or apps from
strangers.
## JavaScript apps [#javascript-apps]
This is the normal way to write an app. They run on the frame inside an embedded
[QuickJS](https://bellard.org/quickjs/) runtime - FrameOS's own fork,
[quickts](https://github.com/FrameOS/quickts), which parses TypeScript and JSX natively (2026.9.0+),
so errors name your own file and line. No Node.js needed on the device, and no
compiler, so your change is live on the frame seconds after you save it. Create one from the code
templates (text, logic, SVG, or image), define its fields in `config.json`, and export plain
functions:
```ts title="app.ts"
export function init(app: FrameOSApp): void {
app.initialized = true
}
export function get(app: FrameOSApp, context: FrameOSContext): string {
return `${app.config.prefix}: ${app.config.message}`
}
```
JS apps can fetch data with `frameos.fetchText()` / `frameos.fetchJson()`, return text, JSON, SVG
markup, or images, and plug into scenes exactly like the built-in ones. They work on every kind of
frame, and they're the **only** option on [ESP32](/guide/esp32) and [Pico](/guide/pico) boards.
An app can be more than one file. Its files are its whole module graph: `app.ts` can
`import { layout } from './helper'` and `import cities from './data.json'`, and the editor
resolves those siblings as you type. Relative imports only - there is no npm on the frame, so a
bare `import x from 'lodash'` fails with a message that says so - and stack traces name the file
and line (`helper.ts:5:3`).
A **select** field in `config.json` (and a select [state field](/guide/scenes#state-fields))
takes either plain strings or `{ "value": "dark", "label": "Dark theme" }` pairs; the value is what
your app sees, the label is what people pick. In the editor's options box that's one `value | Label`
per line.
Scene JavaScript runs sandboxed: bounded memory and run time, no filesystem, and network access
only through the FrameOS fetch helpers.
## Nim apps [#nim-apps]
The built-in apps are written in [Nim](https://nim-lang.org/) and compiled into the frame's binary
* this is how a $15 Pi Zero 2 W stays fast. Reading them is the best way to learn what an app can
do.
Editing that Nim source is a different matter: it only takes effect in a
[compiled scene](/guide/scenes#execution) on a Raspberry Pi, and every change means a rebuild. For
new work, reach for JavaScript first. A minimal render app looks like this:
```python title="color.nim"
import pixie
import options
import frameos/apps
import frameos/types
type
AppConfig* = object
inputImage*: Option[Image]
color*: Color
App* = ref object of AppRoot
appConfig*: AppConfig
proc render*(self: App, context: ExecutionContext, image: Image) =
image.fill(self.appConfig.color)
# called when used as a render app (blue node)
proc run*(self: App, context: ExecutionContext) =
render(self, context, context.image)
# called when used as a data app (green node)
proc get*(self: App, context: ExecutionContext): Image =
result = if self.appConfig.inputImage.isSome:
self.appConfig.inputImage.get()
elif context.hasImage:
newImage(context.image.width, context.image.height)
else:
newImage(self.frameConfig.renderWidth(), self.frameConfig.renderHeight())
render(self, context, result)
```
Things to know:
* The `render` event is your starting point - it fires on the scene's timer, or when dispatched
by another app.
* The `context` carries the `image` you draw on (via [pixie](https://github.com/treeform/pixie))
and the scene's `state`, a standard Nim
[JsonNode](https://nim-lang.org/docs/json.html): read with `state{"field"}.getStr()`, write with
`state{"field"} = %*("value")`.
* State is cleared on every render; use instance variables on the `App` object to persist data
between renders.
* Learn by example: the
[built-in apps](https://github.com/FrameOS/frameos/tree/main/frameos/src/apps),
[types.nim](https://github.com/FrameOS/frameos/blob/main/frameos/src/frameos/types.nim), and the
[utils/](https://github.com/FrameOS/frameos/tree/main/frameos/src/frameos/utils) folder show
what's available.
## Where each kind runs [#where-each-kind-runs]
| | Raspberry Pi | ESP32-S3 | Pico W / ESP32-C3 |
| ---------------------- | ------------------------------------------------- | ------------------------------------- | ----------------------- |
| Built-in apps | All of them | All but Chromium screenshots and RTSP | Rendered on the backend |
| Custom JavaScript apps | ✅ | ✅ | Rendered on the backend |
| Custom Nim apps | Legacy compiled scenes only (self-hosted backend) | ❌ | ❌ |
| Nim code nodes | Legacy compiled scenes only (self-hosted backend) | ❌ - use JS code nodes | ❌ |
## SVG, fonts and text [#svg-fonts-and-text]
The `render/svg` app rasterises SVG markup - a very practical way to lay out a panel, especially
from a JavaScript app that generates the markup. `` elements are typeset with the frame's
own fonts and drawn as outlines, so labels come out sharp at any size - no need to layer
`render/text` on top of an SVG for its labels. `font-family` is matched loosely against
the fonts on the frame - `"PT Sans"`, `PTSans-Bold.ttf` and `font-weight="bold"` all land on
`PTSans-Bold.ttf` - and anything the frame doesn't have falls back to the built-in face rather than
failing the drawing. `text-anchor`, `dominant-baseline`, ``, entities and stroked or
transformed text all work. FrameOS implements a subset of SVG rather than the whole spec: ``
and external references are ignored, a Pi holds at most eight typefaces per SVG, and on a
microcontroller SVG text always renders in the one built-in typeface.
On a Raspberry Pi you can upload your own TrueType fonts under the frame's assets and pick them in
any text or font field. Drop a `NotoColorEmoji.ttf` in there and it's used automatically as a
fallback for characters your font doesn't cover (SVG text excluded - colour emoji are bitmaps with
no outlines). An [ESP32 with an SD card](/guide/esp32#fonts) can load TTFs from `fonts/` on the
card too, one face at a time.
# The backend (/guide/backend)
The backend is the control panel for your frames. You use it to design scenes, deploy them over
SSH, and manage them through [FrameOS Remote](/guide/remote) when they connect back to the
backend. It's a single self-hosted web app that runs on your own machine and needs no account.
Don't want to host anything? [FrameOS Cloud](/guide/cloud) is the hosted alternative, and a
[standalone frame](/guide/standalone) needs neither - the backend is the most capable of the
three, and the one to pick for legacy compiled Nim apps, custom drivers, thin-client boards and a
terminal on the frame.
A few things worth knowing up front:
* The backend needs **network access to your frames** for first deploys and direct status checks
(SSH and HTTP). After [FrameOS Remote](/guide/remote) is installed, frames can also connect
back to the backend over an outbound WebSocket.
* The backend **does not need to stay running**. Frames work fully standalone after a deploy.
Keeping it running gets you log aggregation, metrics, and one-click redeploys.
## Quick install [#quick-install]
The backend is the control panel that **deploys to** your frames - it is not the software that
runs on the frame itself. Run the command below on the machine that will manage your frames:
your laptop, a server, or a NAS. The frame's Raspberry Pi is set up separately in the
[next step](/guide/raspberry), and never listens on port 8989.
The easiest way to install on a Mac or a Debian/Ubuntu Linux machine:
```bash
bash <(curl -fsSL https://frameos.net/install.sh)
```
The script installs Docker if needed, then runs the [`frameos/frameos`](https://hub.docker.com/r/frameos/frameos) container on port `8989`.
Once it's up, open `http://localhost:8989` on that same machine (or `http://:8989` from
another device) and create your local account.
## Home Assistant add-on [#home-assistant-add-on]
If you run Home Assistant, FrameOS is one click away:
1. Go to **Settings → Add-ons → Add-on Store** in Home Assistant.
2. Click the **⋮** menu in the top right corner and select **Repositories**.
3. Add `https://github.com/FrameOS/frameos-home-assistant-addon`.
4. Find **FrameOS** in the add-on list, click **Install**, then **Start**.
5. Optionally enable **Start on boot** and **Watchdog**.
6. Click **Open Web UI**.
The add-on deploys precompiled release binaries; it has no Docker socket, so building FrameOS from
source needs a [build host](/guide/settings#builds-self-hosted-backend-only) configured under
**Settings → Builds**. Browser flashing of ESP32 boards needs the add-on reached over `https://`
or `localhost` - behind plain-`http://` ingress there is no Web Serial.
## Running via Docker manually [#running-via-docker-manually]
This is what the install script does under the hood:
```bash
# generate a stable secret key
SECRET_KEY=$(openssl rand -base64 32)
mkdir -p db
# run the latest release
docker run -d --name frameos --restart always \
-p 8989:8989 \
-v ./db:/app/db \
-e SECRET_KEY="$SECRET_KEY" \
frameos/frameos:latest
```
Then open `http://localhost:8989` and create your local account.
To keep the container automatically up to date, add
[watchtower](https://containrrr.dev/watchtower/):
```bash
docker run -d --name watchtower \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower --interval 86400 frameos
```
### Faster builds with Docker access [#faster-builds-with-docker-access]
SD card image generation works in the default container without extra privileges. If you also want
the backend to **cross-compile FrameOS from source** in local build containers (instead of using
precompiled binaries or building on the device), give it access to Docker:
```bash
SECRET_KEY=$(openssl rand -base64 32)
mkdir -p db /tmp/frameos-cross
docker run -d --name frameos --restart always \
-p 8989:8989 \
-v ./db:/app/db \
-v /tmp/frameos-cross:/tmp/frameos-cross \
-v /var/run/docker.sock:/var/run/docker.sock \
--privileged \
-e TMPDIR=/tmp/frameos-cross \
-e SECRET_KEY="$SECRET_KEY" \
frameos/frameos:latest
```
This is optional, and rarely needed: FrameOS ships **precompiled binaries**, and only
[legacy compiled scenes](/guide/scenes#execution) and distros outside the release matrix build from
source. Remote SSH build servers, or building on the frame itself, are under **Settings → Advanced:
legacy source builds**.
Flashing an [ESP32](/guide/esp32) compiles nothing on any install: the backend writes the signed
release image for the board from your browser, then sets it up over USB.
**Behind a reverse proxy**, set `FRAMEOS_PUBLIC_URL=https://frameos.example` and list the proxy's
address in `FRAMEOS_TRUSTED_PROXIES`. The live logs and the Terminal use WebSockets, which are
refused unless the browser's origin matches the host the backend sees - so the proxy must pass
`Host` through (`proxy_set_header Host $host` in nginx) or send `X-Forwarded-Host`. Without the
trusted-proxy list the login rate limiter also sees every user as the proxy.
## Local development with Flox [#local-development-with-flox]
Use this path when you want to work on FrameOS itself instead of running the released Docker
image. The repository ships a checked-in [Flox](https://flox.dev/) environment that installs the
Python, Node, pnpm, Nim and Redis tooling used by the backend and frontend.
```bash
git clone https://github.com/FrameOS/frameos.git
cd frameos
flox activate
pnpm dev
```
`flox activate` creates a repo-local `.venv`, installs `backend/requirements.txt`, runs
`pnpm install --frozen-lockfile`, and installs the Nim dependencies for both the frame runtime
and FrameOS Remote. The activation hook reruns only when the lockfiles or package definitions
change.
`pnpm dev` opens an `mprocs` dashboard with the local services:
* **backend** - FastAPI on `http://localhost:8989` with `DEBUG=1`.
* **worker** - the ARQ background worker used for deploys and builds.
* **vite** - the live frontend on `http://localhost:8616`, proxying `/api` and `/ws` to the
backend.
* **kea** - frontend type generation in watch mode.
* **redis** - local Redis for jobs, logs, websockets and Remote connections.
Open `http://localhost:8616` once the backend and Vite panes are ready. In development mode the
backend runs database migrations before startup and creates a local `.env` with a development
`SECRET_KEY` if you do not already have one.
If you prefer to run processes manually, start Redis with either the `redis` pane in `pnpm dev`
or Flox services:
```bash
flox services start redis
pnpm run dev:backend
pnpm run dev:worker
pnpm --dir frontend run dev
```
## Link the backend to FrameOS Cloud [#cloud]
A self-hosted backend can optionally be linked to a [FrameOS Cloud](/guide/cloud) account. Open
**Settings → FrameOS Cloud** and click **Connect to cloud.frameos.net**: the backend shows a short
code, you approve it on [cloud.frameos.net/device](https://cloud.frameos.net/device), and the link
is made. What it adds, each an **Enabled feature** you can switch individually:
* **Save and share scenes via the cloud** (always on): the [scene store](/guide/scene-store)
is in every frame's **Add scene** drawer (your private cloud scenes join it under **Private
scenes**), and a **Save to cloud (private)** menu item pushes your own scenes to your account, where they stay private until you publish them.
* **Cloud backups**: after every successful deploy the backend uploads the frame's settings and
scenes, plus your scene library, **end-to-end encrypted** with a key the cloud never sees. Keep
the recovery key (`FRBK1-…`, shown under the switch) in your password manager; after reinstalling
the backend, link it again, paste the key, and **Restore** what you had. There is also a plain
**Download a local backup (.tar.gz)** button that never leaves your machine.
* **Cloud login**: sign in to this backend with **Continue with FrameOS Cloud**. You can then turn
local password login off; if the link ever goes away, the password comes back by itself, so you
can't lock yourself out.
Linking changes nothing about how frames are managed: the backend still deploys over SSH and
[FrameOS Remote](/guide/remote), the cloud never gets a shell or your credentials, and
**Disconnect** (here, or under **Account → Backends** in the cloud) undoes it at any time. The
cloud only learns the backend's version and which features are on.
## Next step [#next-step]
Time to prepare your Raspberry Pi: [set up the Raspberry](/guide/raspberry).
# FrameOS Cloud (/guide/cloud)
[FrameOS Cloud](https://cloud.frameos.net) is the hosted way to run FrameOS. You create an account,
point a frame at it, and manage that frame from a browser: assign scenes from the
[scene store](/guide/scene-store), change settings, watch logs and metrics, queue firmware updates.
There is no backend to install and nothing to keep running at home.
It is **one of three ways to run FrameOS**, next to a [standalone frame](/guide/standalone) and the
[self-hosted backend](/guide/backend). Pick it when you don't want to host anything, or when your
frames live somewhere your laptop isn't. Nothing about it is required: a frame set up through the
cloud keeps rendering its scenes if the cloud is unreachable, and can be moved back to standalone or
self-hosted operation at any time.
FrameOS Cloud is new and still changing. It works - frames enroll, scenes deploy - but expect
rough edges. It is **free while in beta**, with fixed limits (frames, storage, logs) so one
account cannot crowd out the rest. Your data stays yours: scenes, backups and frame settings can
be exported from your account at any time. Found a bug? Report it on
[GitHub](https://github.com/FrameOS/frameos/issues) or [Discord](https://discord.gg/9dT9y7EzUw).
## What the cloud does, and deliberately doesn't [#what-the-cloud-does-and-deliberately-doesnt]
The cloud is identity, store, relay and fleet view. **The frame is the product.** Everything still
renders on the device, from scenes stored on the device, on the device's own schedule.
A cloud-managed frame is also deliberately limited. The cloud:
* **never gets a shell.** There is no SSH, no terminal, no remote command execution. The protocol
between frame and cloud has no verbs for it.
* **never compiles code.** Only sandboxed, [interpreted scenes](/guide/scenes#execution) can be
pushed - the same JavaScript-and-node-graph scenes that run on an ESP32. Scenes that run shell
commands are refused.
* **never proxies your images.** Frames fetch images straight from the source and
[decode them on the device](/guide/images), whatever their size.
* **never renders for you.** Previews are the frame's own last render, or rendered in your browser.
* **only sees logs and metrics if you switch that on**, per frame.
* **never sees your WiFi password.** An SD image built in the cloud is assembled in your browser:
the WiFi credentials and root password are written into the image locally and never sent to
the server. The same goes for an ESP32 flashed from the browser - WiFi goes straight to the
board over USB. The cloud gets the frame name, the display and a claim token, nothing more.
* **never holds your backups in the clear.** Backups from a linked backend are encrypted before
upload with a key only you have; the cloud stores ciphertext.
* **ships no binaries.** Firmware updates are a nudge: the frame downloads the release itself and
verifies its signature before installing.
The local escape hatches stay open. A cloud-managed Pi still serves its
[control page and HTTP API](/guide/control) on port `8787`, and its admin page is where you
disconnect it from the cloud if you ever want to. If a frame loses its cloud link it simply
becomes a standalone frame, still showing the scenes it had.
The cloud itself is open source (AGPL-3.0, in the `cloud/` folder of the
[main repository](https://github.com/FrameOS/frameos)) and the enrollment protocol is documented so
anyone can reimplement it. Running your own copy is possible but not recommended yet - it is built
for exactly one deployment.
## Create an account [#create-an-account]
Sign up at [cloud.frameos.net/signup](https://cloud.frameos.net/signup) with an email address and a
password, or **Continue with Google**. Verify your email and sign in; you land on **Frames**,
your fleet.
The header has three sections: **Frames** (your fleet), **Scenes** (the
[scene store](/guide/scene-store), on `scenes.frameos.net`) and **Account** (linked backends,
**Settings** for service API keys and SSH keys, **AI usage**, backups, activity log, security, and
**Developer** for [API tokens and the MCP server](#developer)).
Under Security you can add a passkey or TOTP two-factor authentication - recommended for an
account that controls physical frames.
## Add a frame [#add-a-frame]
With no frames yet, the Frames page *is* the **Add a frame** panel; later it's the **Add frame**
button. There are four ways in, matching the hardware you have:
### SD card image (Raspberry Pi) [#sd-card-image-raspberry-pi]
The cloud builds a personalized SD image **in your browser**:
1. Pick your board: `raspberry-pi-64` (Zero 2 W, Pi 3, Pi 4), `raspberry-pi-5` (Pi 5, CM5) or
`raspberry-pi-32` (Zero, Zero W, Pi 1).
2. Name the frame, pick its display (or choose to pick it later from the frame's setup hotspot),
and enter your WiFi network. A **Country** field, prefilled from your browser's locale, sets the
radio's regulatory domain - without it a frame can't join access points on 2.4 GHz channels 12
and 13. Optionally set a root password and tick the **SSH keys** that
should be able to log in as root - add one right there or under **Account → Settings**; the
cloud keeps public keys only, and the builder shows how much of the card's 4 KB
configuration area they take. Your browser's time zone is written into the card too, and for
HDMI the panel size is autodetected - only displays with no size in the catalog need a width and
height.
3. Click **Download SD image**.
Your browser downloads the generic release image, verifies its release signature, writes your settings into its boot partition, and
hands you a `.img.gz`. WiFi credentials and the root password **never leave your browser** - only
the frame name, display and a claim token travel to the cloud. Flash the image with
[Raspberry Pi Imager](https://www.raspberrypi.com/software/) ("Use custom image") or
[balenaEtcher](https://etcher.balena.io/), insert it, and power up.
On first boot the Pi joins your WiFi (or raises the `FrameOS-Setup` hotspot if it can't), takes its
hostname from the frame name (slugified: "Kitchen Frame" becomes `kitchen-frame.local`), and enrolls. The panel shows the FrameOS
status screen - name, resolution, network, *Managed via: FrameOS Cloud* - until you give it a scene.
You can flash one image to as many cards as you like. Every frame that enrolls from the card
appears as **pending** - the first one included: open it under **Frames** and click
**Confirm frame** in the banner at the top of its page. Until you do, the frame accepts no scenes or
settings - so a lost SD card can't quietly add frames to your account. By default the image keeps
working forever; tick **Stop this SD card from adding frames after a while** on the builder to give
its claim code an expiry.
### Install script (any Pi, most Linux) [#install-script-any-pi-most-linux]
Already have a Pi running Raspberry Pi OS, or some other Debian-flavoured box? Copy the one-liner
from the **Install script** tile and run it on the device:
```bash
curl -fsSL https://cloud.frameos.net/install.sh -o /tmp/frameos-install.sh && \
sudo FRAMEOS_CLAIM_TOKEN=FRCT_... sh /tmp/frameos-install.sh
```
It asks for the display, time zone and an admin password, installs prebuilt FrameOS binaries, and
enrolls the frame on first start. The claim token is single-use and expires after 24 hours; the
panel mints a fresh one every time you open it.
### Link a frame that already runs [#link-a-frame-that-already-runs]
A frame that is already up - flashed from a [generic image](/guide/standalone), say - can be
claimed without reflashing:
* On the frame's admin page, open **Settings → FrameOS Cloud → Connect to cloud.frameos.net**. The
frame shows a short code like `H7LU-JLWN`. While the link is pending, the same code and a QR code
are drawn **on the panel itself** - over the current scene, or beside the rows on the status
screen.
* Scan the QR, or open **Add frame → Link a frame that already runs** in the cloud, type the code
from the frame, check what is asking (the frame introduces itself by its hostname, kind and
address) and approve. The drawer opens the frame the moment it has enrolled.
[cloud.frameos.net/device](https://cloud.frameos.net/device) still works too.
Afterwards the frame's **FrameOS Cloud** box shows a **Cloud status** - *Linked, not managed* or
*Managed from the cloud* - and two switches: **Manage this frame from FrameOS Cloud** and **Sign in
here with FrameOS Cloud** (2026.9.15+). The admin password sits under the second one, and the two
logins can never both be off. Switching management off and on again re-registers the same frame,
not a second one.
Seeing the code on the display is the proof of ownership, so a frame linked this way is active
right away. In the `FrameOS-Setup` hotspot you can also pick **Manage via: FrameOS Cloud** and leave
the claim code empty - the frame shows a link code on its display as soon as it is online.
### Flash an ESP32 from the browser [#flash-an-esp32-from-the-browser]
Plug an [ESP32-S3 board](/guide/esp32#supported-boards) in over USB, pick the hardware preset
(Waveshare PhotoPainter, TRMNL DIY kit, Seeed reTerminal, CrowPanel…), name the frame, enter WiFi
and click flash. The browser writes the generic release firmware with Web Serial (Chrome or Edge),
then provisions the cloud address, a claim token and your browser's time zone over the serial
console. Either port a board offers works - the chip's *USB JTAG/serial debug unit* or a
*USB Single Serial* bridge such as the CH340 on the Seeed reTerminals (the flasher used to refuse
those). A brand-new board formats its storage on first boot, so the flasher waits up to 90 seconds
for the console prompt and tells you what it saw if it never comes.
ESP32 frames are full frames in the cloud too: scenes render on the chip, with logs, metrics,
previews, schedules and signed over-the-air updates. PSRAM-less boards (ESP32-C3: the original
TRMNL, XTEINK X4) would need the cloud to render every refresh for them, which it doesn't offer
yet - their enrolment is refused. Run those from a [self-hosted backend](/guide/backend).
Every path offers **Start it with the scenes from…** an existing frame, so a second kitchen frame
can be a clone of the first. Scenes are copied; assets on the SD card are not. Every path also
seeds the new frame's [time zone](/guide/settings#time-zone) from the browser you added it in.
## Managing a frame [#managing-a-frame]
Each frame has an overview page at `/frames/`, with its tools as sub-paths - **Settings**,
**Preview**, **Schedule**, **Logs**, **Metrics**, **Assets** and **Activity** (`/frames//logs`) -
and its scenes at `/frames//scenes/`.
* **Scenes.** Install from the [scene store](/guide/scene-store), or open a scene in the built-in
editor - the same node-graph editor the backend has - and click **Save & Deploy**. The frame
applies the change within seconds while connected; pushes to an offline or sleeping frame queue
and land when it reconnects. A frame holds up to 20 scenes. Every scene remembers where it came
from - the drawer says *Installed from ‹scene› · v3* - so you can tell a store scene from your
own edit of it, and which version is on the frame. When the store has a newer version, the
scene's image carries an **Update available** flag; click it, or **Update to latest** in the
scene's **…** menu, and after one confirmation the frame moves to the newest version - order,
pins and key grants are kept (2026.9.20+).
* **Preview.** The frame's own last render, plus per-scene snapshots, fetched while you have the
page open. An ESP32 frame sends its framebuffer on request; a sleeping one delivers it on its
next wake. Fleet tiles can also render a scene in your browser on request.
* **Sleep forecast.** A frame that deep-sleeps says so before it halts: the sidebar and the
frame's page show **asleep · wakes in 5 min** (or **overdue**) rather than a stale "last seen",
and battery frames show their charge in place of the status dot - click it for the charge,
a two-week history and a forecast of the battery's life.
* **Settings.** The set a cloud-managed frame accepts is declarative: name, rotation, render
interval, scaling, time zone, debug logging, the QR control code, metrics interval, HTTP
response cap, palette and partial-refresh policy, GPIO buttons, and on ESP32 the
[power options](/guide/esp32#power) - deep sleep, battery pins and, from firmware 2026.8.39, the
battery enable pin. Everything else about the device - display driver, WiFi, mount points - is
owned by the device. A setting newer than the frame's firmware is shown disabled with the
version it needs.
* **Schedule.** Scene switches, events, and `restart`/`reboot` entries, evaluated on the frame in
its own time zone.
* **Logs and metrics.** Off by default for privacy: flip **Ship logs and metrics to the cloud**
in the frame's settings. Logs keep the last 5000 lines per frame; metrics charts include reboot
markers.
* **Service API keys.** Enter OpenAI, Unsplash, Immich, Home Assistant and similar keys once under
**Account → Settings**. Each frame has a **Deliver keys to this frame** switch, and only receives the
keys its scenes actually ask for, pulled by the device over HTTPS.
* **Assets.** Browse and upload files in the frame's assets folder (its SD card on an ESP32).
* **The ⋯ menu**: rename, render, **Render status screen** (Linux frames), restart, reboot,
**Update firmware** (shown as **Update queued** while the nudge waits for the frame), delete.
Frames on an HDMI/framebuffer display or a HyperPixel also get **Turn display off** / **Turn
display on** (FrameOS 2026.9.18+) - the panel powers down while the frame keeps rendering. Deleting a frame
revokes its link; the device keeps rendering what it has, as a standalone frame.
* **Activity.** An audit trail per frame: connections, pushes, firmware changes, renames.
### Local network access [#local-network-access]
While cloud-managed, a frame blocks HTTP requests to private addresses on your LAN - so a scene
installed from the store can't poke at your router. To allow it (for the Home Assistant app, for
instance), confirm a six-digit code shown on the panel from the frame's local admin page, or run
`set allow_local_network 1` on an ESP32's USB console. It takes a person in front of the frame.
## Scripts and AI agents: API tokens and MCP [#developer]
Everything the browser can do with your account is also available to scripts and AI agents, from
**Account → Developer**:
* **Personal API tokens** (`fc_api_…`, or a read-only `fc_apiro_…`) stand in for your account on
the JSON API. A token can do what you can do in the UI, minus the actions that always need a fresh
sign-in in the browser (revoking frames, approving device links, changing your password).
Minting one needs a recent sign-in too; tokens are shown once and stored hashed. Every token
expires - 90 days by default, a year at most - and all of them are revoked when you reset your
password or add a second factor.
* **A hosted MCP server** at `https://cloud.frameos.net/api/mcp` - the same API with a vocabulary a
[Model Context Protocol](https://modelcontextprotocol.io/) client can read: 73 tools across
frames (list, rename, settings and schedules, assign and activate scenes, screenshots, logs,
metrics, assets, reboot, firmware updates, claim tokens), scenes (read and save versions, create
from JSON, zip or URL, fork, publish, gallery images, yank, delete, convert legacy compiled
scenes), the store, the scene AI, and
a server-side **render** that runs the real FrameOS runtime on any scene JSON and hands the
image back, plus the **lint** that publishing applies. Every tool that changes what a frame
shows, stores or does takes `confirm: true`, so an agent only acts when you asked for it. Nothing local to run: it speaks Streamable
HTTP with your token as a bearer.
```bash
claude mcp add --transport http frameos https://cloud.frameos.net/api/mcp \
--header "Authorization: Bearer fc_api_…"
```
Claude Desktop, Cursor and other clients take the same thing as JSON:
```json
{
"mcpServers": {
"frameos": {
"type": "http",
"url": "https://cloud.frameos.net/api/mcp",
"headers": { "Authorization": "Bearer fc_api_…" }
}
}
}
```
For clients that want a subprocess, the same server runs over stdio from a checkout of the
repository: `FRAMEOS_CLOUD_TOKEN=fc_api_… pnpm --filter @frameos-cloud/mcp start`.
The MCP server is a thin wrapper over the cloud's routes (`cloud/packages/mcp` in the main
repository), so anything it can do, `curl` with the same bearer can do too. For the frame's
*local* API - no account involved - see [Controlling the frame](/guide/control).
## Linking a self-hosted backend [#linking-a-self-hosted-backend]
You can also keep a [self-hosted backend](/guide/backend) and link *it* to your cloud account, for a
few optional extras: signing in to the backend with your cloud account, end-to-end encrypted
backups of your frames and scenes, and saving scenes to your private store. See
[Link the backend to FrameOS Cloud](/guide/backend#cloud). A linked backend still deploys over SSH
and still works with the cloud switched off.
## Limits [#limits]
While in beta: 50 frames per account, 20 scenes per frame, 200 scenes and 100 MB of private scene
storage per account, 100 MB of backups, 100 MB of logs. The account, enrolling and managing frames,
and installing public store scenes are meant to stay free; nothing security-related (two-factor
auth, revocation, signed updates, the sandbox) will ever be paywalled.
## Privacy [#privacy]
The cloud stores your account, the frames you linked (name, hardware, version), the scenes and
settings you give them, and - only if enabled per frame - logs and metrics. Backups from linked
backends are encrypted in the backend before upload, with a key the cloud never sees. Data lives
in the EU. Every sign-in, device link and permission change is listed under **Account → Activity**.
**Download my data** under Security exports everything as JSON, and **Delete my account** is
immediate and needs no support ticket. The full details are in the
[privacy policy](https://cloud.frameos.net/legal/privacy).
## Moving between cloud, backend and standalone [#moving-between-cloud-backend-and-standalone]
A frame is managed by exactly one of them at a time, and switching is always a local action on the
device:
* **Cloud → standalone**: delete the frame in the cloud, or **Disconnect** on the frame's admin
page. The frame keeps its scenes.
* **Standalone → cloud**: [link it](#link-a-frame-that-already-runs) with the code on its display.
* **Standalone → backend**: **Adopt frame** in the backend imports its settings and scenes - see
[First deploy](/guide/first-deploy#add-the-frame).
* **Backend → cloud** and back: disconnect from one side, then enroll or adopt from the other.
A guided migration is planned but not built yet.
# Controlling the frame (/guide/control)
Every frame runs its own web server on port `8787`. This is the heart of FrameOS's design:
viewing, controlling, and even administering a frame works **directly against the device**, from
any browser or script on your network. The backend can be off. The internet can be down. It is
also how a [standalone frame](/guide/standalone) is managed at all, and the escape hatch on a
[cloud-managed](/guide/cloud) one.
[ESP32 frames](/guide/esp32) serve the same kind of API on port `80` - the image, the scene
state, `POST /event/`, logs, metrics and assets - but not the browser UI below. Manage
those from the backend. [Pico](/guide/pico) and [virtual](/guide/virtual) frames have no
on-device server at all.
## The frame's web app [#the-frames-web-app]
Open `http://:8787/` for the frame's own web UI:
* **`/`** - view the current image, live (updates over a WebSocket).
* **`/c`** - the control page: switch scenes and edit the active scene's state fields (the yellow
nodes you added in the [scene editor](/guide/scenes)), including how often it refreshes.
* **`/admin`** - the on-device [admin panel](#the-admin-panel): settings, scenes, schedule, logs,
metrics, assets and updates. A full management UI served by the frame itself.
By default the frame also renders its control URL as a **QR code on the display**, so anyone
standing in front of the frame can scan and control it.
## The admin panel [#the-admin-panel]
`http://:8787/admin` is the frame's own management UI. It is the same interface the
[backend](/guide/backend) shows for a frame, just served from the device: sign in with the admin
password you set during setup (or in the backend under **Settings → Admin**) and you get:
* **Scenes** - what is installed and what is active. Install scenes from the
[scene store](/guide/scene-store) - it is built into the **Add scene** drawer (FrameOS
2026.9.10+; the bundled samples stand in when the store can't be reached) - upload a scene ZIP, or open the
node-graph [scene editor](/guide/scenes) right here. Interpreted scenes save and go live
without a compile step.
* **Settings** - everything from [Useful settings](/guide/settings): name, display driver and
rotation, time zone, [access mode](#access-modes) and the QR code, the color palette, GPIO
buttons, the HTTP API key, HTTPS, and which backend or cloud manages the frame. This is also
where you **connect a standalone frame to a backend** with an adoption code, link it to
[FrameOS Cloud](/guide/cloud), or disconnect it again.
* **Preview** - the current image, live over a WebSocket.
* **Schedule** - render intervals and cron-like [schedules](#scheduling), plus the maintenance
restart and reboot timers.
* **Logs** and **Metrics** - runtime output and health charts (CPU, memory, temperature, render
times), straight from the device. Logs only leave the frame if you switch on
*Send logs to backend*.
* **Assets** - files on the frame's SD card: fonts, images, downloaded scenes.
* **Debug** - on-device diagnostics. (**Ping** and **Terminal** exist only in the backend.)
* **Settings → FrameOS upgrade** - the frame downloads a new release itself, verifies the
signature, swaps it in and restarts - rebooting only when the new release needs it. Cloud and
backend only *nudge* it; the binary never passes through them.
The frame's **…** menu at the top right re-renders, restarts, **reboots the device** and brings
back the status screen (**Render status screen**). Frames on an HDMI/framebuffer display or a
HyperPixel also get **Turn display off** / **Turn display on** there (2026.9.18+).
The admin panel is how a [standalone frame](/guide/standalone) is managed at all, and it stays
available on frames that a backend or the cloud manages - which is why the frame can always be
[moved between the three](/guide/cloud#moving-between-cloud-backend-and-standalone) without a
reflash. [ESP32 frames](/guide/esp32#what-it-cannot-do) do not serve it: they answer the HTTP API
and a small setup page, and are managed from the backend or cloud.
## Access modes [#access-modes]
Under the frame's settings you choose who can do what without a key:
* **Private** (default) - a key is required to view and to control.
* **Protected** - anyone on the network can view the image; the key is required to change
anything.
* **Public** - no key needed for anything. Makes for the smallest QR codes.
The access key travels as a `?k=...` query parameter (or a cookie after the first visit).
Traffic is plain HTTP on your local network. If you need real security - frames on a shared
network, remote access - put the frames on a [Tailscale](https://tailscale.com/) tailnet or
enable [HTTPS](/guide/settings#https) in the frame's settings - the frame serves it itself, on
port 8443.
## The HTTP API [#the-http-api]
Everything the control page does is a plain HTTP call. With `KEY` being your access key:
```bash
# the current image as PNG
curl "http://frame:8787/image?k=KEY" -o now.png
# the active scene and its state
curl "http://frame:8787/state?k=KEY"
# all scenes and their states
curl "http://frame:8787/states?k=KEY"
# change state fields on the active scene and re-render
curl -X POST "http://frame:8787/event/setSceneState?k=KEY" \
-H "Content-Type: application/json" \
-d '{"render": true, "state": {"message": "Dinner at 7!"}}'
# switch to another scene
curl -X POST "http://frame:8787/event/setCurrentScene?k=KEY" \
-H "Content-Type: application/json" \
-d '{"sceneId": ""}'
# trigger a render right now
curl -X POST "http://frame:8787/event/render?k=KEY" -d '{}'
```
`POST /event/` dispatches any event into the running scene graph - the same mechanism
buttons, schedules, and apps use internally. This makes integrations trivial: a Home Assistant
automation, a cron job, a shell script, or a doorbell webhook can all drive your frame.
There's also a full admin REST API (config, logs, metrics, restart, reboot) under
`http://frame:8787/api/` behind the admin login, and a WebSocket at `/ws` that streams renders.
The events that control the device itself - `reboot`, `restart`, `reload` and `uploadScenes` - need
the admin login or the backend's API key too; the access key (`?k=`) can't trigger them.
From FrameOS 2026.9.13, a request that changes something and carries a browser `Origin` header must
come from the frame's own address, so a web page on another site can't post to your frame. `curl`,
scripts and Home Assistant send no `Origin` and are unaffected. Behind your own reverse proxy, pass
`Host` through or send `X-Forwarded-Host`.
## Scheduling [#scheduling]
Frames have their own on-device scheduler: render intervals per scene, plus cron-like schedules
(minute/hour/weekday) that can switch scenes or fire events - for example, a calendar during the
day and art in the evening. Configure it under the frame's **Schedule** tab. The schedule runs on
the frame, in the frame's own [time zone](/guide/settings#time-zone), so it keeps working with the
backend off. When in doubt about what the frame will do next, check **Logs**: on start and after
every schedule change it logs `scheduler:loaded` with its zone, local time and the next due entry
("Mon 01:02 reboot"), and every firing logs `scheduler:fire`.
The same panel has a **Maintenance** card that schedules a `restart` of the FrameOS
process or a full `reboot` of the device - the successor to the nightly-reboot setting, on both
Raspberry Pi and ESP32 frames.
# The ESP32 (/guide/esp32)
FrameOS runs on **ESP32-S3 microcontrollers** - no Linux, no SD card, no SSH. The backend (or
[FrameOS Cloud](/guide/cloud#flash-an-esp32-from-the-browser)) flashes a firmware image over USB
straight from the browser, and from then on the device fetches scenes over WiFi, renders them
**itself**, drives the e-paper panel, and updates its own firmware over the air.
This is not a cut-down mode. The same scene interpreter that runs on a Raspberry Pi runs on the
S3, with the same apps, the same JavaScript runtime, the same image pipeline, and the same
assets, logs, metrics and scheduler the backend shows for a Pi frame. What a $6 board *cannot*
do is a short, specific list - it's [at the bottom of this page](#what-it-cannot-do).
An **ESP32-S3** can be set up and managed from either a [self-hosted backend](/guide/backend)
or [FrameOS Cloud](/guide/cloud). The cloud flashes the generic release firmware from your
browser and pushes interpreted scenes. The **thin-client** boards below need the self-hosted
backend, as Cloud does not support virtual frames. There is no standalone mode:
an ESP32 frame has no admin page of its own.
An **ESP32-S3 with PSRAM** renders scenes on the device itself. A **PSRAM-less board**
(ESP32-C3, and the [Pico W boards](/guide/pico)) can't hold a renderer, so it runs as a *thin
client*: the backend renders and dithers the scene and the device streams the finished panel
bytes. Both are set up the same way and look identical in the backend.
## What you need [#what-you-need]
* **An ESP32-S3 board with PSRAM.** Rendering happens in PSRAM, so don't skip it. 8 MB is
enough for every supported panel, including the 13.3" 1600×1200 six-colour ones - the render
canvas is 16-bit and a 13.3" scene fits in 3.7 MB ([details](#performance)).
The reference bare board is the
[Seeed XIAO ESP32-S3](https://www.seeedstudio.com/XIAO-ESP32S3-p-5627.html)
(8 MB flash, 8 MB octal PSRAM), but most people start from an
[all-in-one board](#supported-boards) that already has a panel attached.
* **A panel**: any Waveshare-family SPI e-paper panel, wired over SPI (plus one tiny I2C OLED) -
or no display at all.
The 10.3" IT8951 and 12.48" panels use different controller stacks and still need a Pi.
* **The [backend](/guide/backend)** or [FrameOS Cloud](/guide/cloud), to flash the board and
design scenes. Nothing is compiled: both write the signed
[release](https://github.com/FrameOS/frameos/releases) image for your chip and flash size, then
configure the board over USB.
## Supported boards [#supported-boards]
FrameOS ships **hardware presets** - pick one in the backend and the panel, GPIO map, buttons,
battery divider, SD slot and flash size are all filled in for you:
| Preset | Chip | Panel | Notes |
| ------------------------------------------------------------------------------ | ---- | ------------------------------- | -------------------------------------------------------------------------------------------- |
| [Waveshare ESP32-S3 PhotoPainter](/devices/waveshare-esp32-s3-photopainter) | S3 | 7.3" Spectra 6 | 8 MB PSRAM, TF card slot, battery header, 2 buttons |
| [Waveshare ESP32-S3 ePaper 13.3E6](/devices/waveshare-esp32-s3-epaper-13in3e6) | S3 | 13.3" Spectra 6 | 16 MB PSRAM, 32 MB flash, TF card, battery sensing |
| [Seeed reTerminal E1001](/devices/seeed-reterminal-e1001) | S3 | 7.5" black/white | 3 front buttons, 32 MB flash, battery sensing |
| [Seeed reTerminal E1002](/devices/seeed-reterminal-e1002) | S3 | 7.3" Spectra 6 | 3 front buttons, 32 MB flash, battery sensing |
| [Seeed reTerminal E1004](/devices/seeed-reterminal-e1004) | S3 | 13.3" Spectra 6, portrait | 8 MB PSRAM, 32 MB flash, TF card, 3 buttons, 5000 mAh battery - sleeps on battery by default |
| [Seeed reTerminal Sticky](/devices/seeed-reterminal-sticky) | S3 | 3.97" black/white | Small desk/fridge frame |
| [Elecrow CrowPanel 5.79"](/devices/elecrow-crowpanel-5in79) | S3 | 5.79" 4-grey | 5 buttons, dual SSD1683 |
| [TRMNL 7.5" DIY Kit](/devices/trmnl-og-diy-kit) | S3 | 7.5" black/white | XIAO ePaper driver board |
| [TRMNL 4.26" DIY Kit](/devices/trmnl-4in26-diy-kit) | S3 | 4.26" 4-grey | XIAO ePaper driver board |
| [TRMNL OG](/devices/trmnl-og) | C3 | 7.5" black/white | Thin client - backend renders |
| [TRMNL BWRY](/devices/trmnl-bwry) | C3 | 7.5" 4-colour | Thin client - backend renders |
| [XTEINK X4](/devices/xteink-x4) | C3 | 4.26" 4-grey | Thin client - backend renders |
| [ESP32-C3 0.42" OLED board](/devices/esp32-c3-oled-0in42) | C3 | 0.42" SSD1306 OLED, 72×40 (I2C) | The tiny HW-675 / 01Space dev board, 4 MB flash, BOOT button - thin client |
Anything else is a **Custom ESP32 board**: pick the panel, set the pins, done. The
[Pimoroni Inky Frame](/guide/pico) boards carry a Pico instead of an ESP32 and have
[their own page](/guide/pico).
## Flash the device [#flash-the-device]
1. Click **New frame** in the backend and choose **Flash embedded device**.
2. Pick the platform (**ESP32-S3** or **ESP32-C3**), your hardware preset or panel, and enter
your WiFi credentials.
3. Connect the board over USB on the **Connect over USB** card. It reads the board and offers the
next step: **Flash FrameOS & set up this frame** on a blank board, **Set up as this frame** on
one that runs FrameOS but isn't provisioned yet, and **Update over USB** / **Apply frame settings** later on.
4. The browser writes the published release image that matches the board's chip and flash size (it
reads the flash id, so a 16 MB board gets the 16 MB layout and a 4 MB C3 its own)
and then sends this frame's settings over the USB console - preset, panel, pins, WiFi, backend
address, API key, hostname, HTTP response limit and admin login. What a console line can't carry
(an HTTPS certificate, service keys, the schedule) arrives with the first settings sync over
WiFi. Nothing is compiled, so a first flash takes about a minute. This uses Web Serial, so it works
in Chrome and Edge, on `https://` or `localhost` - a backend served over plain `http://` from
another machine (the Home Assistant add-on behind ingress, for instance) has no Web Serial; the
Logs panel tells you when that's the case. Either USB port a board offers works: the chip's own
*USB JTAG/serial debug unit*, or a *USB Single Serial* UART bridge like the CH340 on the Seeed
reTerminals (firmware 2026.8.38+; older firmware only answered on the JTAG port, which those
boards don't wire up). Or download the image from the
[release](https://github.com/FrameOS/frameos/releases) and flash it by hand - there is one per
chip and flash layout (`-generic` is 8 MB on an S3 and 4 MB on a C3, next to `-4mb`,
`-8mb`, `-16mb` and `-32mb`):
```bash
esptool.py --chip esp32s3 --port /dev/tty.usbmodem* --baud 460800 \
--flash_size 8MB write_flash 0x0 frameos--esp32-s3-generic.bin
```
The device boots fully provisioned: it joins your WiFi, registers with the backend, pulls its
scenes, and renders.
### Provisioning by hand [#provisioning-by-hand]
A board flashed by hand with a release image can be configured without the backend's flasher.
Every supported panel driver is compiled into every image - you pick the panel at runtime:
* **Captive portal**: an unprovisioned device raises a WiFi hotspot called `FrameOS-XXXX`. Join
it and any web page redirects to the setup form (WiFi, backend URL, frame ID and API key,
panel, GPIO pins). The hotspot is WPA2-protected with a 10-character passphrase generated per
device - it's on the status screen, `config` prints it on the serial console, and `set ap_psk`
changes it (firmware 2026.9.1+; older firmware raised an open network). Requests from inside the
hotspot skip the admin login - the passphrase on the panel is the credential - so a forgotten
admin password no longer means a factory reset to change WiFi (2026.9.5+). Until then the panel
shows the FrameOS status screen with the hotspot's instructions; once provisioned but without scenes, it shows the frame's name, panel, IP and
who manages it.
* **Serial console** (115200 baud) - always available, and quickest for tinkering. It answers on
both of the ports a board can bring out, the S3's built-in USB-Serial/JTAG device and UART0
behind a USB-UART bridge:
```
frameos> status # chip, panel, WiFi, memory, NVS use, hostname, power policy
frameos> set hardware waveshare_esp32_s3_photopainter # apply a board preset: panel, pins, buttons, battery wiring
frameos> wifi MySSID MyPassword # saves and reboots
frameos> set time_zone Europe/Brussels # IANA name; the tz data slice is fetched once online
frameos> set panel EPD_7in5_V2
frameos> set pins rst=5,dc=4,cs=3,cs2=-1,busy=6,sck=7,mosi=9,pwr=-1
frameos> render # render right now
frameos> ota # check for a firmware update
frameos> ota downgrade # let the next update be an older release, once (2026.9.10+)
frameos> factory-reset # wipes scenes, schedule, cached data and all settings
```
## Scenes on a microcontroller [#scenes-on-a-microcontroller]
Install scenes exactly like on any other frame. The device syncs them from the backend as JSON
and runs them **on-device**: the same interpreter as Linux frames, with the standard apps
compiled into the firmware and code nodes, expressions and JavaScript apps running on an embedded
[QuickJS](https://bellard.org/quickjs/) (the [quickts](https://github.com/FrameOS/quickts) fork,
which parses TypeScript itself). Scene changes arrive over WiFi in seconds - no
reflashing, no compiler.
Scenes are stored one file per scene, so a frame can hold many of them (up to 32, or 512 KB
of scene JSON) while keeping only the active one parsed in memory. Switching scenes - from the
backend, an event, a button or the schedule - loads the new one and drops the old. A scene that
ran the board out of memory is skipped at the next boot - the frame starts on the next scene
instead of looping (2026.9.9+).
**Every built-in app works** except two that need a real operating system: *Chromium screenshot*
and *RTSP camera snapshot*. Calendars, weather, Home Assistant sensors, OpenAI text and images,
Immich, Google Photos, Unsplash, Wikimedia Commons, QR codes, charts, SVG, gradients, splits,
zoom/pan - all of it runs on the microcontroller, over TLS, straight from the device.
### Any-size images, natively [#any-size-images-natively]
This is the part people don't expect from a microcontroller: **you can point an ESP32 frame at a
4000×3000 photo and it just renders it.** No proxy, no resize server, no "please host a smaller
version".
A naive decode of that JPEG needs 48 MB of pixels - roughly six times the PSRAM on the
board. FrameOS never materialises it. The decoder scales *while* it decodes, straight into the
panel-sized canvas: a rolling window of compressed bytes goes in, correctly box-filtered target
rows come out, and the peak cost is the canvas plus a few hundred kilobytes. Since 2026.8.39 the
bytes go from the socket into the decoder directly - the file is never held in PSRAM or written to
flash at all. It works for baseline JPEG, PNG, WebP, BMP and PPM, and it applies to the whole chain
* a rotation or a resize downstream tells the decoder how few pixels it actually needs before a
single byte is fetched. Formats that can't stream (progressive JPEG, GIF) are buffered instead,
spilling to the SD card when they don't fit.
There's a whole page on how this works and why it matters:
[Big images on small devices](/guide/images).
## Assets and the SD card [#assets-and-the-sd-card]
Boards with a TF/SD slot mount it at `/srv/assets`, and it behaves like the assets folder on a
Pi:
* Downloaded images are cached there instead of being re-fetched (and re-paid for) every render.
* Browse, upload, rename and delete files from the frame's **Assets** tab in the backend.
* The **Local image** app plays files or folders straight off the card.
* Downloads that have to be held whole - a progressive JPEG, a GIF, anything that isn't an
image - spool to the card's `.cache` folder when they don't fit in memory, and the frame streams
them from there. The only ceiling is the frame's
[HTTP response size setting](/guide/settings#http-response-size) (4 MiB by default; raise it
for big sources). Without a card, whatever is free in the frame's internal storage stands in -
about half a megabyte on a typical 8 MB board, more on 16 or 32 MB flash, nothing on 4 MB.
Baseline JPEGs and PNGs don't need any of this: they decode straight off the socket.
* A `.ttf` dropped into `fonts/` on the card shows up in the editor's font picker for that frame;
**Sync fonts** copies the backend's font store over in one go. See [Fonts](#fonts).
Boards without a card slot work fine - the cache and the spool degrade to memory, and nothing
starts failing that used to work.
## Fonts [#fonts]
The firmware carries one typeface (Ubuntu Regular). It can also use fonts from the SD card: the frame lists `fonts/*.ttf` on the card through `GET /api/fonts`, the editor's font
picker offers them, and the renderer loads the named face off the card - one parsed face at a time,
files over 1 MB refused unparsed, falling back to the built-in face rather than losing the
drawing. Upload them through the **Assets** tab or use **Sync fonts**, which skips files the card
already has. A frame without a card, or one that's short of render memory, renders in the built-in
face.
Two things stay Pi-only: SVG `` always uses the built-in face on a microcontroller
(a second parsed font would come out of the heap the renderer needs), and there is no emoji
fallback - `NotoColorEmoji.ttf` is 10 MB of bitmaps with no outlines to draw.
## Updates, control and telemetry [#updates-control-and-telemetry]
* **Over-the-air updates**: A/B partitions with automatic rollback - if a new build fails to come
up, the next reset boots the previous one. The device checks the backend daily, or on demand,
and verifies the image's signature against the FrameOS release key before switching to it - the
backend and the cloud both relay the published release for the board's own flash layout, never
a build of their own (firmware 2026.9.2+). It refuses a release older than the one it runs
unless you arm `ota downgrade` on the console. A sleeping frame
stays awake for the download, logs its progress, and gives up on a release after three failed
attempts rather than re-downloading it on every wake. (The 4 MB flash profile has no OTA
slot; those boards update over USB.)
* **A real HTTP API on the device**: `GET /status`, `/image`, `/state`, `/states`, and
`POST /event/` to dispatch any event into the running scene, plus logs, metrics and asset
routes under `/api/frames//` - much the same surface the
[Pi's local API](/guide/control) exposes. Native HTTPS turns on after the frame's first settings
sync, when it has a certificate to serve.
* **Logs and metrics** stream to the backend, and the last 128 lines are kept in a ring buffer on
the device, readable over the serial console when the network is down.
* **An on-device scheduler**: per-scene render intervals plus minute/hour/weekday schedules that
switch scenes, fire events, or restart/reboot the device (a **Maintenance** entry in the
Schedule panel), evaluated on the device in the frame's
[time zone](/guide/settings#time-zone) (firmware 2026.8.34+; earlier firmware runs on UTC). The
JavaScript `format()` helper follows the frame's time zone too, from 2026.8.44.
* **Buttons**: GPIO buttons (the board presets know where they are) dispatch scene events, so a
press can change a scene, re-render, or run anything you wire up in the editor.
## Battery and deep sleep [#power]
This is where a microcontroller frame beats a Pi. Configure it under the frame's **Power**
settings, or on the console:
| Setting | What it does |
| --------------------------------- | ---------------------------------------------------------------------------------------------- |
| `deep_sleep` | Power the chip down between refreshes and cold-boot for the next one |
| `deep_sleep_on_battery` | Only sleep while running on a battery; stay connected on USB power |
| `wake_schedule` | Align wakes to wall-clock boundaries, so a clock or calendar updates *on* the hour |
| `wake_check` | Wake at least this often to pick up queued commands, without refreshing the panel |
| `battery_pin` / `battery_divider` | Read cell voltage on an ADC1 pin; percentage shows in the backend |
| `battery_enable_pin` | The GPIO that switches the divider on for a reading, on boards that gate it (`-1` = always on) |
Without `wake_schedule` the time already spent awake - boot, WiFi, render - is subtracted from
the interval, so the cadence doesn't drift by however long the render took. Below 3% battery the
frame skips the refresh and sleeps for six hours rather than cycling a flat cell into damage - and
it takes two readings a moment apart and two passes in a row to believe it, so one noisy ADC
sample can't park a healthy cell in the six-hour sleep.
**Buttons wake the frame.** Every GPIO button on a wake-capable pin is armed as a wake source
next to the timer (2026.8.42+). A press brings the frame up early, replays as an ordinary button
event to the scene, and that pass renders - so "next photo" works on a sleeping frame. While
asleep the WiFi radio is in modem sleep, the clock is kept by the RTC (no time-sync wait on wake),
and the log queue and the battery reading are flushed to the backend or cloud before the chip
halts, so a battery frame's history is complete.
The backend and the cloud both know when a frame is sleeping: right before it halts, the frame
announces when it will be back, and the frame list shows **asleep · wakes in 5 min** (or
**overdue** when the wake never came) instead of a stale "last seen". Battery frames show their
charge in place of the status dot. Click it for a popup with charge, voltage, how often the frame
wakes, drain per day, a two-week history with a forecast to empty, and a slider that shows the
battery life at another wake cadence. A lone reading far below its neighbours is an ADC misread,
not a discharge: from firmware 2026.9.0 the frame reads the cell in rounds and reports the value it
believes, and the charts leave the rest out and say how many misreads they ignored. Anything you deploy meanwhile is queued and lands on the next
wake. On the 13.3" E1004 a heavy scene keeps the frame awake for 80-90 s per cycle: \~23 s to
render, \~22 s to dither and pack, \~30 s for the Spectra 6 refresh.
The board presets carry the battery wiring - the PhotoPainter and 13.3E6 headers, the Seeed
reTerminals' switched divider, the XIAO ePaper driver board, the TRMNL and the XTEINK X4 (only the
E1004's is verified on hardware so far) - and the [reTerminal E1004](/devices/seeed-reterminal-e1004)
preset also switches on `deep_sleep_on_battery` with a 15-minute `wake_check`, so it sleeps on its
5000 mAh cell and stays connected on USB out of the box. A frame provisioned before its preset
knew the wiring keeps `battery_pin -1` until you re-run `set hardware` on the console or fill in the
three values under **Power**.
## How fast is it? [#performance]
Slower than a Pi, and mostly irrelevant - the panel is the bottleneck either way. Measured on the
7.3" Spectra 6 PhotoPainter board (8 MB PSRAM), rendering *Weather*, the heaviest sample
scene:
| Step | Time |
| ---------------------------------------- | ----------------------- |
| Cold-boot scene load + JS transpile | \~3.3 s (once, at boot) |
| SVG rasterisation | 7-8 s |
| Dither + pack to the Spectra 6 palette | \~3.2 s |
| Panel refresh (the panel's own hardware) | \~29 s |
Simpler scenes are much quicker; a black/white panel dithers and refreshes in a fraction of that.
A frame that updates a few times an hour, or a few times a day on battery, never notices.
Memory on the same board: 7.1 MB PSRAM free with a scene resident, 4.2 MB still free at
the worst point of a render. If a panel is too large for a board's PSRAM, the backend says so
before you flash rather than leaving you with a frame that can't render.
The scene canvas is claimed once from PSRAM before WiFi starts and reused for every render. Its
format is decided per board: **full colour when a canvas fits in half the module's PSRAM,
16-bit RGB565 otherwise.** An 800×480 panel on 8 MB and a 1200×1600 panel on 16 MB
render in full colour; a 1200×1600 panel on an 8 MB module - the
[reTerminal E1004](/devices/seeed-reterminal-e1004) - is the one that takes the 16-bit canvas
(3.7 MB instead of 7.3 MB), which is what lets it render on-device at all. The 16-bit
canvas dithers its stores so gradients stay smooth, and since every panel the firmware drives is
dithered e-paper, a photo dithered to Spectra 6 through either canvas lands the same palette
within 0.05% per colour. The Pi, HDMI and the browser preview keep a full RGBA canvas.
## Wiring [#wiring]
Default pins target the XIAO ESP32-S3 (a board preset overrides all of these):
| Signal | GPIO | XIAO label |
| ------ | ---- | ---------- |
| CS | 3 | D2 |
| DC | 4 | D3 |
| RST | 5 | D4 |
| BUSY | 6 | D5 |
| SCK | 7 | D8 |
| MOSI | 9 | D10 |
Remap at runtime with `set pins rst=5,dc=4,cs=3,busy=6,sck=7,mosi=9,pwr=-1` on the serial
console, in the captive portal, or per-frame in the backend. The 13.3" Spectra 6 panel has two
controllers and needs `cs2` as well.
## What it cannot do [#what-it-cannot-do]
Straight, so you can decide before you buy:
* **Panels**: Waveshare-family SPI e-paper (and one small I2C OLED) only. No HDMI, no LCD, no Pimoroni Inky HATs, no
Python-backed drivers. The 10.3" IT8951 and the 12.48" multi-controller panels need a Pi.
* **Two apps are missing**: *Chromium screenshot* and *RTSP camera snapshot*. Both need to launch
a real program on a real OS.
* **No Nim app editing.** Scenes run [interpreted](/guide/scenes#execution), like everywhere else.
You can write and fork apps in **JavaScript/TypeScript** and use JS code nodes, but editing an
app's Nim source means compiling, which means a Pi. The backend warns you when a scene contains
Nim, and offers **Convert to an interpreted scene** to port it to JavaScript.
* **Fonts are limited.** One built-in typeface, plus TTFs from the SD card loaded one at a time
([Fonts](#fonts)). No emoji fallback, and SVG `` always renders in the built-in face.
* **No on-device admin web app.** The frame answers the HTTP API and serves a setup page, but the
full `/admin` panel and the control page with the QR code are Pi-side. Manage ESP32 frames from
the backend. (`GET /status` does report the chip, the hardware preset, and internal heap and
largest-block figures - useful when a thin client runs out of memory.)
* **It's slow.** Seconds per render, not milliseconds. Fine for e-ink, useless for anything that
needs to move.
* **PSRAM is the ceiling.** No PSRAM means thin-client mode, where the backend has to be reachable
at render time.
## Raspberry Pi or ESP32? [#raspberry-pi-or-esp32]
| | Raspberry Pi | ESP32-S3 |
| ----------- | ------------------------------------------ | ------------------------------------------------ |
| Displays | [120+ drivers](/devices): e-ink, HDMI, LCD | Waveshare SPI e-paper, every driver in one image |
| Custom code | JavaScript, or Nim in a compiled scene | JavaScript/TypeScript |
| Apps | All of them | All but Chromium screenshots and RTSP |
| Fonts | Any TTF you upload, emoji fallback | Built-in typeface, plus TTFs on the SD card |
| Power | \~1 W, wall-powered | Deep sleep between refreshes - battery is viable |
| Updates | SSH / Remote deploys | Signed OTA with A/B rollback |
| Price | \~$15-20 (Zero 2 W) | \~$6-10 (bare S3), or an all-in-one board |
In short: pick the Pi for the broadest hardware and the deepest customisation, and the ESP32 for
a battery-powered, self-contained frame - or because you already own one of the
[all-in-one boards](#supported-boards). Want to help port a panel? See the
[firmware README](https://github.com/FrameOS/frameos/tree/main/embedded/esp32) and say hi on
[Discord](https://discord.gg/9dT9y7EzUw).
## Next step [#next-step]
[Deploy your first scene](/guide/first-deploy).
# First deploy (/guide/first-deploy)
This page is written for the [self-hosted backend](/guide/backend). It assumes you have it running and a
[Raspberry Pi reachable over SSH](/guide/raspberry) (or booted from a FrameOS SD image).
Using an [ESP32](/guide/esp32) instead? Flash it first - the scenes and deploy flow below work
the same way. On a [standalone frame](/guide/standalone) or in [FrameOS Cloud](/guide/cloud)
the frame is already added; skip to [installing scenes](#install-some-scenes), which looks the
same in all three.
## Add the frame [#add-the-frame]
Click "New frame" in the backend and pick how this frame gets set up:
* **Download SD card** - build a preconfigured [Raspberry Pi image](/guide/raspberry).
* **Install over SSH** - deploy to a Pi the backend can already reach. Enter its IP or hostname
and SSH credentials (`user:password@host` in one go works too).
* **Install with a script** - one command to paste on the device when SSH isn't an option.
* **Flash embedded device** - build and flash [ESP32](/guide/esp32) or [Pico](/guide/pico)
firmware, straight from the browser.
* **Virtual frame** - [no hardware at all](/guide/virtual); the backend renders to an image URL.
* **Import frame** - restore a frame you exported earlier as JSON. The export contains every
secret of the frame, so keep it safe.
* **Adopt frame** - take over a Pi that is already running FrameOS on its own (booted from a
generic SD image, say). Give the backend the frame's address and its admin login; it imports the
frame's settings and scenes, keeps its access key so existing links work, and points the frame
at this backend, restarting it so logs start flowing right away. Adoption goes over the frame's
own admin API (backend 2026.9.12+): a generic-image card has no SSH and no
[Remote](/guide/remote), so the deploy drawer says "No shell on this frame", deploys are fast
deploys over that API, and **Update FrameOS** runs the frame's own signed upgrade. Scenes,
settings, assets, fonts, service keys, logs, restart and reboot all work; the Terminal doesn't.
The admin login is the backend's only way in, so it can't be switched off on such a frame. ESP32 frames
can't be adopted - flash those from the backend instead.
Then name the frame and select the right **display driver** for your panel - there are over 120 to
choose from, organised by vendor. Pick "Web only" if you just want to try FrameOS without any
display hardware. HDMI frames detect their display's resolution themselves.
Before a scene arrives, the frame shows the **FrameOS status screen**: its name, display and
resolution, network address, a live clock and time zone, who manages it and "Waiting for the
backend to deploy a scene." Once it has scenes, that same screen lists them, an **Inputs** row
names the GPIO buttons and input drivers it has, and it notes the last GPIO button pressed - handy
when a panel looks blank and you want to know whether the frame is fine, or whether a button is
wired right. Bring it back any time with **Render status screen** in the frame's **…** menu.
## Install some scenes [#install-some-scenes]
Install a few scenes onto the frame from the [scene store](/guide/scene-store) - **Add scene →
Scene store**. Good first picks:
* **Gallery scenes** like *Made in Space* or *Masterpieces* - curated art that needs no
configuration.
* **Sample scenes** like *Calendar*, *Message board*, or *Unsplash image* - some need API keys,
which you can add under the backend's **Settings** page.
## Deploy [#deploy]
Press **Deploy**. The backend shows a deploy plan - what gets uploaded, what gets installed - and
streams the progress log. The first deploy takes the longest (it installs dependencies and uploads
the FrameOS binary); subsequent deploys are fast and only ship what changed. Editing a scene
afterwards is quicker still - [scenes are interpreted](/guide/scenes#execution), so nothing is
rebuilt, and [ESP32](/guide/esp32) frames simply receive the new scene JSON over WiFi.
When it finishes, a preview image appears in the backend, followed by the same image on the frame
itself.
## Control the frame [#control-the-frame]
Scenes expose controls - a calendar URL, a message to display, a color. Change them from the
frame's dashboard in the backend, or directly on the frame itself:
* Open the **Control URL** (shown under the frame's settings) from any browser on your network.
* Or scan the **QR code** shown on the frame.
In the backend, clicking a frame in the left rail unfolds its tools right under it - **Scenes,
Settings, Preview, Schedule, Logs, Metrics, Assets** - and each has its own URL
(`/frames//logs`), so you can bookmark a frame's logs.
The frame serves this control page itself, on port `8787`. The backend doesn't need to be
running - see [Controlling the frame](/guide/control) for the full local HTTP API.
## Next step [#next-step]
Build your own scene in the [scene editor](/guide/scenes).
# Big images on small devices (/guide/images)
Frames are small computers. A Pi Zero 2 W has 512 MB of RAM; an
[ESP32-S3](/guide/esp32) has 8. A single 4000×3000 photo, decoded the ordinary way, is
**48 MB of pixels** - six times everything the microcontroller has, and enough to push a Pi
Zero into swapping and dying.
The usual workarounds are all bad. Resize everything by hand. Run a proxy server that shrinks
images before the frame sees them. Add `?width=800` to every URL and hope the host honours it.
Each one puts a machine between your frame and the thing it's showing, which is exactly what
FrameOS exists to avoid.
So FrameOS does the other thing: **it decodes big images without ever holding them.**
This is not a setting. Point any app at any image and it works. This page exists so you know
why it works, and where the real limits are.
## Scale while decoding, not after [#scale-while-decoding-not-after]
The naive pipeline is *decode → resize → draw*, and the peak cost is the decode. FrameOS folds
the resize into the decoder: it reads the compressed bytes through a small rolling window and
writes finished, correctly scaled rows straight into the frame's canvas. The full-size image
never exists.
What that costs on an 800×480 frame:
| | Ordinary decode | FrameOS |
| ------------------------------------- | -------------------------- | ------------------------- |
| 4000×3000 JPEG | 48 MB | canvas + a few hundred KB |
| 2.6 MB PNG, 1024×1024 | 4 MB + the compressed body | streamed, \~64 KB window |
| 1920×1280 photo through a resize node | 9.8 MB | 540 KB |
Streaming decoders exist for **JPEG, PNG, WebP, BMP and PPM**. GIF, QOI, TIFF and SVG decode the
regular way, bounded by a memory budget (below). SVG rasterises straight into the canvas, and its
gradients are painted in row strips sized to the memory that's actually free, so a full-panel sky
never asks for a full-panel scratch image.
The scaling is a proper area filter - each output pixel is the average of the source pixels it
covers - so downscaling stays smooth. It matters more than it sounds: nearest-neighbour
decimation of line art (an XKCD comic, a chart, a thin font) deletes whole columns of pixels and
eats letter stems. Axes that aren't being downscaled are untouched, byte for byte.
## Ask for only what you need [#ask-for-only-what-you-need]
The frame knows how many pixels it can actually show, and that number travels **up** the scene
graph before anything is fetched.
If a photo feeds a rotate node feeding an 800×480 canvas, the rotation tells the decoder the
useful resolution is 480×800 - so a 1920×1280 source decodes at 450×300 instead of full size. A
zoom/pan node asks for its canvas times its maximum zoom, and no more. Where a node draws
directly onto the canvas, the decoder is handed *the canvas itself* as its output buffer, so
there is no intermediate image at all.
The result is that adding nodes to a scene doesn't ratchet memory up the way it would in a
conventional pipeline, and the pixels you get are the pixels you would have got the expensive
way.
## Spool to storage, cache to storage [#spool-to-storage-cache-to-storage]
Two more tiers, both automatic:
* **On an ESP32, images decode straight off the socket.** When the download has a known target,
the bytes go from the network into the streaming decoder and out as canvas rows - no copy of the
file in PSRAM, none on flash. A 2 MB PNG on a 13.3" board with 1.5 MB free renders fine.
* **Downloads that must be held whole spool to disk** - the SD card on a Pi, the TF card on an
ESP32 board - and the decoder streams from the file. The image cache does the same: a cached
photo that's too big for memory is kept on the card as raw pixels, so a 4-hour cache actually
caches instead of silently re-downloading every render. On the bench, serving a cached 13.3"
image from the card takes 7 seconds against 23 for the miss.
* **Large text feeds are folded as they arrive.** A multi-megabyte iCal subscription used to be
buffered whole to extract a handful of events; now it's read a line at a time through an 8 KB
window, with events outside your export range dropped as they're parsed.
Frames with no writable storage lose the tiers, not the feature - everything falls back to
memory, and nothing that used to work starts failing.
## When it really is too big [#when-it-really-is-too-big]
Every decode is planned against the memory the device actually has free at that moment - both
the total and the largest contiguous block, because a fragmented heap can have megabytes free and
no room for one channel buffer. If the plan doesn't fit, FrameOS doesn't error out - it
**degrades**: it retries at half resolution, then a quarter, skipping any rung that still wouldn't
fit, and upscales the result. A slightly soft image beats an error frame on a wall. Every degraded
render is logged, and a microcontroller that finds an aborted decode has leaked half its heap
restarts rather than showing soft photos for a day.
Beyond that, honest limits:
* **Progressive JPEGs and interlaced PNGs can't be streamed** - the format interleaves the whole
image, so there is nothing to stream. They're decoded buffered and refused, gracefully, if they
don't fit. Save photos as baseline JPEG if you control the source.
* **Some panels are the problem.** A 13.3" 1600×1200 canvas is 7.7 MB of RGBA on its own,
before any image exists. On an 8 MB ESP32 that panel gets a 16-bit canvas (3.8 MB),
which is what lets it render there at all - but it's still half the PSRAM before the first byte
of a photo arrives. Boards with room to spare (an 800×480 panel, or 16 MB of PSRAM) keep the
full-colour canvas.
* **On a microcontroller, a failed allocation is contained, not fatal.** The render fails, the
device keeps running, and the next render tries again.
## Why not just use a proxy? [#why-not-just-use-a-proxy]
Because a frame that depends on a resize server is a frame that stops working when the server
does - and the whole point of FrameOS is that the frame keeps working when everything else is
off. Image proxying has been built here before, and taken back out again. The fix for "this image
is too big" is always on the device.
If you want to dig into the design and the measurements behind all of this, the engineering
record lives in
[docs/value-pipeline.md](https://github.com/FrameOS/frameos/blob/main/docs/value-pipeline.md) in
the main repo.
# What is FrameOS? (/guide)
FrameOS turns a Raspberry Pi (or an [ESP32](/guide/esp32)) and a display into a
**single-function smart frame**: a kitchen calendar, a hallway dashboard, a daily art frame, a
meeting room display, a thermostat - anything that should show one thing, reliably, for years.
It works with screens that update **60 times per second** (HDMI, LCD) and with screens that update
**once every 60 seconds or slower** (e-ink). Over a hundred display drivers are built in, covering
most Waveshare and Pimoroni e-ink panels, plus anything you can plug into HDMI.
## How it works [#how-it-works]
The **frame** runs a single compiled binary (written in [Nim](https://nim-lang.org/)) on a
Raspberry Pi - or as firmware on an [ESP32-S3](/guide/esp32). It renders scenes, drives the
display, keeps its own schedule, and serves a small web app + HTTP API so you can view and
control it from your phone or any browser. Everything runs **on the device**: nothing stops
working when your internet goes down, and nothing stops working if you turn off whatever manages
the frame.
What manages the frame is up to you. There are three ways to run FrameOS, and a frame can move
between them without reflashing:
| | Best for | What it is |
| --------------------------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **[A standalone frame](/guide/standalone)** | One frame, nothing else | Flash a FrameOS SD image to a Pi, set it up from your phone, manage it from the admin page the frame serves itself. No backend, no account. |
| **[The self-hosted backend](/guide/backend)** | Tinkerers, many frames, custom Nim | A Docker container (or Home Assistant add-on) on your laptop, server or NAS. Designs scenes, flashes ESP32 boards, deploys over SSH, and can be switched off afterwards. |
| **[FrameOS Cloud](/guide/cloud)** | Nothing to host, frames anywhere | A hosted account at cloud.frameos.net: build an SD image or flash an ESP32 from the browser, then assign scenes from the [scene store](/guide/scene-store). Free while in beta. |
The cloud is an option, not a requirement - and a deliberately limited one. It never gets a shell
on your frame, never compiles code, and only pushes sandboxed interpreted scenes. The
[self-hosted backend](/guide/backend) is the most capable of the three, and the project's promise
is that it stays that way.
## Pick your hardware [#pick-your-hardware]
| | Best for | Notes |
| ---------------------------------------------------- | --------------------------------- | -------------------------------------------------------------------------------------------------------- |
| **[Raspberry Pi](/guide/raspberry)** | Everything | 120+ panels, HDMI and LCD, any font, custom apps in JavaScript or Nim. Zero 2 W upward, Pi 5 for 60 FPS |
| **[ESP32-S3](/guide/esp32)** | Battery frames, all-in-one boards | Renders scenes on-device, deep-sleeps between refreshes, updates over the air. Waveshare SPI e-paper |
| **[ESP32-C3](/guide/esp32) / [Pico W](/guide/pico)** | Boards without PSRAM | Thin clients: the backend renders, the device draws. Covers the TRMNL, XTEINK X4 and Pimoroni Inky Frame |
| **[Virtual frame](/guide/virtual)** | No hardware at all | The backend renders to an image URL and a kiosk page - for tablets, browsers and signage players |
With a standalone or cloud-managed Pi you need nothing else. ESP32, Pico and virtual frames are
set up from a [backend](/guide/backend) or from [FrameOS Cloud](/guide/cloud) (ESP32 only).
If you're starting from nothing, get a **Raspberry Pi Zero 2 W** and an e-ink panel: it's the
best-supported combination and nothing is off limits. Choose an **ESP32-S3** if you want a
frame that runs off a battery, or if you already own one of the all-in-one boards like the
Waveshare PhotoPainter, a TRMNL or a Seeed reTerminal.
## What you get [#what-you-get]
* **A visual scene editor** with \~40 built-in apps - calendars, weather, Home Assistant sensors,
photo galleries, Immich and Google Photos, OpenAI text and images, QR codes, charts, SVG - and
every one of them forkable down to its source.
* **Scenes that run on the device**, deployed in seconds without a rebuild, with live state fields
you can change from your phone without redeploying at all.
* **[Images of any size](/guide/images)**, decoded straight into the panel. A 12-megapixel photo
renders on a microcontroller with 8 MB of RAM.
* **A control page and HTTP API on every frame**, on port 8787, plus a QR code on the display so
anyone standing in front of it can take over.
* **Prebuilt SD images** for the Pi and browser flashing for the ESP32, both preconfigured with
your WiFi - from the backend or straight from the cloud.
* **A [scene store](/guide/scene-store)** of community scenes with live previews, installable on
any frame.
* **[3D-printable cases](/cases)** generated around your exact panel and board.
## Getting started [#getting-started]
Pick the path that fits, then deploy a scene:
* **Standalone frame**: [flash a generic FrameOS image](/guide/standalone), join the `FrameOS-Setup`
hotspot from your phone, pick **Nothing (standalone)**, and open the frame's admin page.
* **Self-hosted**: [install the backend](/guide/backend) on **your computer or server** (not on
the frame's Raspberry Pi) with one command, then
[set up the Pi](/guide/raspberry) or [flash an ESP32](/guide/esp32):
```bash
bash <(curl -fsSL https://frameos.net/install.sh)
```
* **Cloud**: [create an account](/guide/cloud) at cloud.frameos.net and click **Add frame**; the
SD image or ESP32 firmware comes out of your browser already enrolled.
Then [deploy your first scene](/guide/first-deploy) - pick from the built-in gallery and sample
scenes or the [scene store](/guide/scene-store), then make your own in the
[scene editor](/guide/scenes). For a professional finish, [3D print a custom case](/cases).
## Project status [#project-status]
FrameOS is open source ([AGPL-3.0](https://github.com/FrameOS/frameos/blob/main/LICENSE)) and under
very active development, with versioned releases on
[GitHub](https://github.com/FrameOS/frameos/releases) and [Docker Hub](https://hub.docker.com/r/frameos/frameos) (`frameos/frameos`). It's
stable enough that frames run for months unattended - but expect the occasional rough edge, and
[report it](https://github.com/FrameOS/frameos/issues) when you find one.
* Join the [FrameOS Discord](https://discord.gg/9dT9y7EzUw)
* Subscribe to [the newsletter](https://buttondown.com/frameos)
* Star us on [GitHub](https://github.com/FrameOS/frameos)
# Pico W & Inky Frame (/guide/pico)
FrameOS also runs on the **Raspberry Pi Pico W** (RP2040) and **Pico 2 W** (RP2350) - most
usefully on the [Pimoroni Inky Frame](https://shop.pimoroni.com/), which carries a Pico on the
back of the panel and is otherwise a lovely piece of hardware with nowhere to run.
Pico frames need a [self-hosted backend](/guide/backend) - it renders every frame for them.
[FrameOS Cloud](/guide/cloud) does not support virtual frames, and thus does not yet render for thin clients.
Therefor a Pico can't be managed from the cloud. Neither can it run [standalone](/guide/standalone).
A Pico W has 2 MB of flash and 264 KB of RAM - it cannot hold a renderer, a
JavaScript engine and a font. So it doesn't: **the backend renders the scene** and the device
streams the finished, packed panel bytes straight into the e-paper controller. No framebuffer
is ever allocated on the device. Unlike an [ESP32-S3](/guide/esp32) frame, a Pico frame needs
the backend to be reachable when it renders.
## Supported boards [#supported-boards]
| Preset | Board | Panel |
| --------------------------------------------------------------------- | -------- | ------------------ |
| [Inky Frame 4.0"](/devices/pimoroni-inky-frame-4) | Pico W | 640×400, 7 colour |
| [Inky Frame 5.7"](/devices/pimoroni-inky-frame-5-7) | Pico W | 600×448, 7 colour |
| [Inky Frame 7.3"](/devices/pimoroni-inky-frame-7-3) | Pico W | 800×480, 7 colour |
| [Inky Frame 7.3" (2024)](/devices/pimoroni-inky-frame-7-3-pico2) | Pico 2 W | 800×480, 7 colour |
| [Inky Frame 7.3" Spectra 6](/devices/pimoroni-inky-frame-7-3-spectra) | Pico 2 W | 800×480, Spectra 6 |
A bare Pico W or Pico 2 W wired to a supported panel works too - set the pins and the panel on
the serial console instead of picking a preset.
## Flash it [#flash-it]
The Pico firmware is a plain UF2, the same for every board of a given chip - the backend does not
build per-frame images for it.
1. Download `frameos--pico-w.uf2` (or `-pico-2w.uf2`) from the
[latest release](https://github.com/FrameOS/frameos/releases).
2. Hold **BOOTSEL** while plugging the board into USB, then copy the `.uf2` onto the `RPI-RP2`
(or `RP2350`) drive that appears. The board reboots into FrameOS.
3. In the backend, add a frame with **Flash embedded device** and pick your Inky Frame preset.
Note the frame's id and API key from its settings.
4. Open the USB serial console at 115200 baud and provision it:
```
frameos> set hardware pimoroni_inky_frame_5_7
frameos> set backend http://192.168.1.10:8989
frameos> set frame_id 42
frameos> set api_key
frameos> wifi MySSID MyPassword # saves and reboots
frameos> status
frameos> render # fetch and refresh now
```
`https://` backends work: TLS 1.2 with hostname verification against a built-in bundle of the
major public CA roots.
## Battery mode [#battery-mode]
`set deep_sleep 1` powers the board off completely after each render, using the Inky Frame's
onboard RTC and power latch - around 20 µA between refreshes. It cold-boots on the next
interval, or when someone presses a front button. On USB power the latch can't cut the supply, so
the firmware sleeps in place instead; the behaviour is the same either way.
## Limitations [#limitations]
The Pico support is newer than the rest and honest about it:
* **The backend must be up when the frame renders.** No backend, no picture. If you want a frame
that renders standalone, use a [Pi](/guide/raspberry) or an [ESP32-S3](/guide/esp32).
* **Front buttons trigger an immediate render**, but per-button scene actions aren't wired up
yet.
* **No on-device scenes, JavaScript, assets or scheduler** - all of that happens on the backend
side for a thin client. The backend dithers the render to the panel's palette before packing
it, so thin clients look the same as a frame that renders on-device.
* The 4.0" Inky Frame's panel initialisation still wants validation on real glass; if yours comes
up mirrored or offset, [tell us](https://github.com/FrameOS/frameos/issues).
Details and build instructions live in the
[Pico firmware README](https://github.com/FrameOS/frameos/tree/main/embedded/pico).
# The Raspberry Pi (/guide/raspberry)
Check the [device-specific instructions](/devices) for your display - a few panels need extra
steps.
A Raspberry Pi frame works [standalone](/guide/standalone), with a
[self-hosted backend](/guide/backend), or with [FrameOS Cloud](/guide/cloud). Option 1 below
covers all three; options 2 and 3 describe the backend's SSH and script installs (the cloud has
its own [install script](/guide/cloud#install-script-any-pi-most-linux) for a stock OS).
## Which Pi to choose? [#which-pi-to-choose]
* **Raspberry Pi Zero 2 W** - the sweet spot for e-ink frames. Four 64-bit cores, tiny, \~1 W of
power, around $15-20. This is what most FrameOS frames run on.
* **Raspberry Pi 5 / 4** - use these for fast HDMI/LCD output (up to 60 FPS) or when size doesn't
matter. The Pi 5 and CM5 have their own SD image. Over HDMI the frame adopts the display's
native mode on its own - plug in a 1080p or 4K screen and it renders at that resolution, with the
status screen's fonts scaled to match; there's no width or height to type in.
* **Raspberry Pi Zero W (v1), Pi 1, CM1** - a single 32-bit ARMv6 core. Slower, but
supported all the way down, prebuilt SD image included.
Not a Pi person, or dreaming of a battery-powered frame? FrameOS also runs on the
[ESP32-S3 microcontroller](/guide/esp32), on [Pico W boards](/guide/pico), and with
[no hardware at all](/guide/virtual).
## Option 1: prebuilt FrameOS SD image (recommended) [#option-1-prebuilt-frameos-sd-image-recommended]
FrameOS ships purpose-built SD card images: a minimal Buildroot-based Linux that boots straight
into FrameOS. No Raspberry Pi OS, no manual SSH setup, nothing to install afterwards.
There are three, one per board family - each covers every board in its family, so you match a
family rather than an exact model:
| Image | Boards |
| ----------------- | -------------------------------------- |
| `raspberry-pi-64` | Zero 2 W, Pi 3, Pi 4, CM3/CM4 |
| `raspberry-pi-5` | Pi 5, CM5 |
| `raspberry-pi-32` | Zero, Zero W, Pi 1, CM1 (32-bit ARMv6) |
You can get an image three ways:
* **Generate it in the backend**: when you add a new frame, choose **Download SD card**. Pick your
board, and the image comes out preconfigured with your WiFi credentials, timezone, display
driver and frame settings - flash it, insert, done. Whether root login is enabled, with which
password and which [SSH keys](/guide/settings#ssh-keys), is an explicit choice on that screen.
* **Build it in [FrameOS Cloud](/guide/cloud#sd-card-image-raspberry-pi)**: the same, but the
image is assembled in your browser and the frame enrolls with your cloud account on first boot.
* **Download a generic image** from the
[latest release](https://github.com/FrameOS/frameos/releases) (look for
`frameos-*-raspberry-pi-64-buildroot.img.gz` and its siblings). On first boot the frame starts a
WiFi hotspot called `FrameOS-Setup` (password `frame1234`). Join it from your phone, enter your
WiFi credentials, and the frame connects to your network. Generic images ship with
no [FrameOS Remote](/guide/remote) and no root password, since there is no backend to talk to yet;
**Adopt frame** in the backend turns a running generic-image frame into a managed one over the
frame's own admin API - scenes, settings, assets, restart, reboot and the frame's signed
self-update all work, SSH deploys and the Terminal don't. Or skip the backend altogether and run it as a
[standalone frame](/guide/standalone).
Flash the image with [Raspberry Pi Imager](https://www.raspberrypi.com/software/) ("Use custom
image"), [balenaEtcher](https://etcher.balena.io/), or plain `dd`. The partitions expand to fill
your SD card on first boot.
On first boot an HDMI frame shows its progress on screen - "Checking network…", then
"Network connected. Loading scenes…" - and any frame without scenes shows the FrameOS status
screen: name, display and resolution, IP address, a live clock with its time zone, who manages
it, the frame's URL and, once you press one, the last GPIO button.
SD-image frames can also **upgrade themselves in place**: the frame downloads a new release,
verifies its signature against a key baked into the build, swaps it in and restarts (rebooting
only when the new release needs it) - no re-flashing, no SSH.
## Option 2: stock Raspberry Pi OS Lite [#option-2-stock-raspberry-pi-os-lite]
If you prefer a standard OS underneath - or you want to run other things on the same Pi:
1. Download the [Raspberry Pi Imager](https://www.raspberrypi.com/software/) and pick the **Lite**
version of Raspberry Pi OS (no desktop needed).
* 64-bit for Pi 3, 4, 5, Zero 2 W.
* 32-bit for Pi 1, 2 and the original Zero W.
2. When prompted, **edit the OS customisation settings**: set a hostname, your WiFi credentials,
and enable SSH. Use a password, or better, generate an SSH keypair and paste the public key.
3. Write the card, insert it into the Pi, connect the display, and power up.
4. Wait for the Pi to appear on your network, then verify you can reach it:
```bash
ping frame.local # or check your router's client list for the IP
ssh pi@frame.local
```
5. Once connected, bring the OS up to date:
```bash
sudo apt update -y && sudo apt upgrade -y
```
That's all. Given SSH access, the FrameOS backend does the rest - including installing
dependencies and (for most targets) deploying **precompiled binaries**, so there's no slow
on-device compilation.
## Option 3: no SSH? install with a script [#option-3-no-ssh-install-with-a-script]
When the backend can't reach the Pi over SSH - a locked-down machine, a different network, a
device you only have a keyboard on - add the frame with **Install with a script**. The backend
hands you a single command to paste on the device. It installs FrameOS, starts
[FrameOS Remote](/guide/remote), and the frame connects **back** to the backend over an outbound
WebSocket. Deploys, logs, metrics and the terminal all work from there without an inbound route.
## Changing the display from the command line [#changing-the-display-from-the-command-line]
The FrameOS binary has a small CLI for the occasions when you have SSH but no web UI - switching
panels on a generic image, say. `set-display` patches the device and optional size, rotation, VCOM
and upload URL into `frame.json`, and `driver-setup` applies any OS-level changes the new driver
needs:
```bash
cd /srv/frameos/current
./frameos set-display --device=waveshare.EPD_7in5_V2 --rotate=180
./frameos driver-setup
```
## Next step [#next-step]
[Deploy your first scene](/guide/first-deploy).
# FrameOS Remote (/guide/remote)
Frames can optionally run **FrameOS Remote**, a small companion process that opens an outbound
WebSocket connection to your backend. Older FrameOS releases and some old docs called this the
"agent"; the current name is Remote.
Remote connects a frame to a [self-hosted backend](/guide/backend). It plays no part in
[FrameOS Cloud](/guide/cloud), which talks to frames over its own protocol and deliberately has
no shell or terminal - and a [standalone frame](/guide/standalone) has nothing to connect to.
Remote means:
* The backend can manage frames **without inbound SSH access** once Remote is installed - useful
for frames behind NAT, on another network, or with SSH locked down.
* Logs and metrics stream to the backend in real time.
* The backend's **Terminal** panel can open a browser shell on the frame.
* Deploy, restart and maintenance actions can go through the existing Remote connection.
Remote is installed and updated automatically when you deploy to a Raspberry Pi frame. SD card
images built in the backend carry it from first boot; the generic release images don't (a generic
card you [adopt](/guide/first-deploy) is managed over the frame's own admin API instead). It authenticates with a per-frame
shared secret, so a Remote connection only controls the frame it belongs to.
Like everything else in FrameOS, Remote is optional. A frame renders fine without Remote, without
SSH, and without the backend running. Remote exists to make managing frames easier, especially
when they are no longer on the same network as your laptop.
# The scene store (/guide/scene-store)
[scenes.frameos.net](https://scenes.frameos.net) is the FrameOS scene store: calendars, dashboards,
galleries, message boards, generative art - published by the community and by the FrameOS
project, and installable on any frame.
Each scene page is a small workspace with four panels you can toggle - **Info** (publisher,
install count, the FrameOS version it needs, description and gallery), **Editor** (the same
node-graph [scene editor](/guide/scenes) the backend has, so you can read exactly what a scene
does before you install it), **AI** ([edit it in plain language](#ai)) and **Preview**, a **live
preview rendered in your browser**. A version dropdown in the bar swaps the whole workspace to any
published version, and **Download .zip** hands you the scene as a file.
The preview is the real FrameOS renderer compiled to WebAssembly, and it can show the scene the
way a panel would: tick **Dither** and pick the panel to simulate - Spectra 6, 7-colour ACeP,
black/white/red, black/white/yellow, 4 or 16 greys, plain black and white - and the frame is
dithered with the device's own algorithm. It has a browser-only **assets folder** (kept in your
browser, seeded with sample photos) so slideshow and SD-card scenes render too, an **Auto apply**
switch that re-renders as you change state fields, and an opt-in real-time mode for scenes that
want to animate. JavaScript clocks run in the scene's time zone. A scene that calls a
pay-per-request service (OpenAI, say) never renders on its own - it waits for you to click
**Run preview** each time the scene changes, so a browse never spends your credits. Tick
**Memory limit** and pick a device (ESP32 with 8 or 16 MB, Pi Zero, Pi) to render under that
frame's memory: the preview shows the peak and says "out of memory" where the frame would.
## Install a scene [#install-a-scene]
The **Install** button opens one dialog with every way in:
* **On a cloud-managed frame**: sign in, pick the frame and click **Install** - the scene is
added next to the frame's existing scenes and deployed within seconds (or as soon as an offline
frame reconnects). The frame remembers where the scene came from and which version it is on.
When a newer version is published, the scene's image says **Update available** - on the cloud, a
backend and the frame's own admin page - and **Update to latest** in its **…** menu takes it
there (2026.9.20+).
* **On a self-hosted backend**: copy the **Install on a self-hosted FrameOS** link and paste it
into the search box of a frame's **Add scene** drawer in the backend. You rarely need the link:
**Add scene → Scene store** lists the public store on every backend, linked to a cloud account or
not, grouped into the same shelves as the store front.
* **On a standalone frame**: open **Add scene** on the frame's admin page - the store is listed
there first (FrameOS 2026.9.10+). **Download zip** and upload still works on older frames.
Installing a store scene on a backend grants it the service keys it declares; revoke them under
the frame's settings, **Service keys for store scenes**.
Scenes that run shell commands on the frame are flagged in the store. A backend warns before
installing one; the cloud never pushes them at all - install those from the frame itself, and only
from publishers you trust. From 2026.9.11 a frame refuses the Chromium screenshot and RTSP snapshot
apps in any store scene until a local admin allows shell apps for store scenes in the frame's
settings, confirmed with the code on the panel.
## Your scenes [#your-scenes]
Signed in, the store gets a second tab, **My scenes**
([scenes.frameos.net/my-scenes](https://scenes.frameos.net/my-scenes)), with your private scenes
first and your public ones below. Everything private is yours alone until you click
**Make public**. Scenes get here five ways:
* **New blank scene** - an empty scene in the hosted editor.
* **Create a scene with AI** - describe what you want and the [scene assistant](#ai) builds it as
a new private scene.
* **Fork** any store scene ("Save a copy as a new private scene in your account"), then edit it in
the hosted editor.
* **Upload a scene ZIP** exported from a backend or a frame; the cover image in the zip becomes
the scene's preview. A zip holding a [legacy compiled scene](/guide/scenes#execution) (Nim code
nodes or Nim apps) is refused - run it through the
[converter](https://scenes.frameos.net/nim-converter) first.
* From a [linked backend](/guide/backend#cloud): the **Save to cloud (private)** item in a scene's
**…** menu pushes it straight to your account, and your private scenes appear under **Add scene →
Private scenes** on every machine you link.
A private scene's install link carries a sharing secret, so you can hand it to a friend without
publishing. A version is the whole scene - diagram, description, tags, category and the image
gallery - so editing the listing or reordering images shows up as **Unsaved changes** and lands
with the next save. Unsaved edits are kept in your browser: leave a scene page and come back, and
the editor restores them with a **Discard** button next to them. Versions are immutable - **Save as new version** asks for a one-line "What changed?"
that heads the version in the dropdown and on the public page, and you can yank a bad version so
new installs skip it. A scene the store has pulled stays readable and deletable by its owner.
## Build and edit scenes with AI [#ai]
Every scene you own has an **AI** panel (and the store's front page has a **Create a scene with
AI** card). The assistant can't deploy on its own: installing on a frame from the chat is an
**Approve** card you click. **Account → AI usage** shows what you've used and has a **Turn AI
off** switch. Describe the scene, or the change - "add a theme selector", "make the clock bigger,
show the weather for Tallinn" - and the assistant edits the node graph, the JavaScript apps and
the state fields, checks the result against a catalog-aware linter and renders it inline before
handing it over; you then save it as a new version, with the prompt pre-filled as the change
note. Long edits keep running if your connection drops and pick up where they were. The prompt is
never stored inside the scene, so the scenes.json you download or publish doesn't carry it. The
same assistant is available to scripts and agents through the
[cloud's MCP server](/guide/cloud#developer).
Published scenes, private ones included, pass an automated content check on their name,
description and preview image. Limits while in beta: 200 scenes per account, 100 MB of private
storage (public scenes are free), 8 MB per scene ZIP.
## Publishing for others [#publishing-for-others]
Make a scene public and it appears in the store under your publisher page. Give it a good preview
image (in the **Preview** panel, **Save to images** uploads the current render to the scene's
gallery; **Download PNG** hands you the same frame as a file; drag gallery images to reorder them,
the first one leads) and a description that says which API keys it needs - it doubles as the
summary in the card Discord and Slack unfurl for the scene's link. There are no ratings or comments
by design - install counts and the diagram speak for the scene.
# Editing scenes (/guide/scenes)
A **scene** is what a frame shows: a graph of connected nodes that runs on the device. Scenes are
built in a visual editor, then deployed onto the frame, where it runs.
The same editor ships in the [self-hosted backend](/guide/backend), in
[FrameOS Cloud](/guide/cloud), and on a [standalone frame's](/guide/standalone) own admin page.
Everything here works in all three, except [compiled scenes](#execution), which need the
self-hosted backend.
## Create a scene [#create-a-scene]
Click **+ Add new scene**, give it a name, and open the editor.
## The four node colors [#the-four-node-colors]
* 🟥 **Red - events.** Entry points that trigger execution. The most important one is `render`,
which fires at a set interval (every 60 seconds, every hour, whatever you choose). Others react
to button presses, schedules, or custom events.
* 🟦 **Blue - render apps.** Draw things: images, text, calendars, gradients, split layouts.
* 🟩 **Green - data apps.** Fetch and transform data: download an image, query Home Assistant,
parse an iCal feed, generate an image with OpenAI, take a screenshot of a web page.
* 🟨 **Yellow - state fields.** User-editable values (text, numbers, options) that can be changed
live from the control page without redeploying.
Drag from the `render` event's connector to add a blue **Render image** app:
Then drag its **image** field out and pick a green data app to feed it - use **FrameOS Gallery**
if unsure. Press deploy:
## Caching [#caching]
Every data app has cache controls. A scene might re-render every minute while its data app only
fetches a new image every 15 minutes - the cached value is reused in between. And if a re-render
produces the same image as before, an e-ink display won't refresh at all: no unnecessary flashing.
## State fields [#state-fields]
Add a yellow **state field** under the scene's "State" tab, then drag it onto the canvas and wire
it into any app input. Deploy once, and from then on you can change the value live - from the
frame's control page, the backend, or the [HTTP API](/guide/control).
Fields are typed, and the type decides what the control page shows: text, long text, numbers,
booleans, a colour picker, a date picker, a select box, raw JSON, a font picker, an image, a
scene picker - or a **path**, which is a file and folder browser over the frame's own
[assets](/guide/settings#assets). "Which photo folder should this play?" becomes a picker instead
of a typed-in string. A select's options are one per line, either a plain value or `value | Label`
when what the frame stores and what people read should differ. Write a literal pipe as `\|`.
Every scene also has one field you don't add yourself: **Refresh interval (seconds)**, listed last
wherever the scene's fields show up - the control page, the backend, the cloud, schedules and
previews (FrameOS 2026.9.20+). It starts at the interval from the scene's settings; change it like
any other field and the next sleep follows. To decide where it sits, what it's called, or to hide
it (`access: private`), add your own numeric field and switch on **Use as refresh interval**. In
code it is `state.refreshInterval`. The **Next sleep duration** app is still a one-time override:
it sets how long to sleep after *this* render, and the render after that is back on the interval.
## Code nodes [#code-nodes]
For quick data plumbing there are inline **code nodes** (green when not selected): single
JavaScript or TypeScript expressions that combine or transform values mid-graph - string
concatenation, math, formatting. (A [legacy compiled scene](#execution) may still carry Nim
expressions; those nodes show a "Legacy Nim" chip with a **Convert scene** button.)
## How scenes run [#execution]
Scenes are **interpreted**: the frame runs the graph as you drew it. New scenes are created that
way, every scene FrameOS ships is built that way, and it's the only way [ESP32](/guide/esp32) and
[Pico](/guide/pico) frames can work at all. What you get:
* **Deploys are near-instant** - nothing is recompiled, so a change is on the frame in seconds.
* **Code nodes are JavaScript, TypeScript or JSX**, and apps can be customised in JavaScript.
* It costs a little more per render, which matters very little on a display that takes 20 seconds
to refresh anyway.
There is a second, older mode - **compiled** - which turns the scene into native code inside the
frame's binary. It is a **legacy** path: it's the only mode that can run custom **Nim** app source
and Nim code nodes, but it forces a FrameOS source build on every deploy, and it only works on a
Raspberry Pi deployed from a [self-hosted backend](/guide/backend). The cloud refuses compiled
scenes outright, at upload and at install. Nothing in the editor creates one any more: new code
nodes are always JavaScript, and saving or installing a Nim-only app asks before it flips a scene
to compiled. Compiled scenes wear an amber **legacy compiled** tag - on the frame list, in the
deploy drawer and in the scene sidebar.
The way out is the converter: **Convert to an interpreted scene**, in the scene's menu, in Scene
Settings and on any "Legacy Nim" node. It ports Nim code nodes and scene-local Nim apps to
JavaScript - simple expressions convert deterministically, the rest goes through an AI pass, and
whatever can't be ported is reported as needing a manual port. It converts the editor's unsaved
copy in place, so check the result, then save or deploy. The Nim does not survive the conversion -
export the scene first if you want a backup. A backend forwards the conversion to the cloud, using
your OpenAI key from **Settings → OpenAI** when one is set. The same converter is a public page at
[scenes.frameos.net/nim-converter](https://scenes.frameos.net/nim-converter) (paste scene JSON, no
login) and a `scene_convert` MCP tool.
A scene is interpreted unless it says otherwise: templates, imports, backups and AI-built scenes
that carry no execution mode all run interpreted. A scene that carries Nim without saying it is
compiled stays interpreted too, and Scene Settings flags the Nim it can't run. The **Execution**
select and the frame's *Installation mode* live under an **Advanced** disclosure on a Pi frame.
Build new scenes interpreted unless you specifically need Nim. If you switch an existing scene
between modes, do a full deploy afterwards; the editor warns you when a scene still contains Nim
source that interpreted mode can't run.
On the frame side there are two ways a binary gets built: **precompiled** (the release binary plus
its driver libraries, scenes interpreted - the default) and **static** (everything compiled into
one binary, which is what a compiled scene forces). Frames still configured with the retired
`shared` or `shared-scenes` modes are moved to `static` automatically.
## Preview before you deploy [#preview-before-you-deploy]
Two ways to see a scene without committing to it:
* **Preview in browser** runs the scene *in your browser*. The frame's renderer is compiled to
WebAssembly, so you get the real thing - same apps, same layout engine, same resolution -
without deploying to a device, or before you own one. It comes with a **Browser assets** folder
that lives only in your browser (seeded with sample photos, so slideshow and SD-card scenes
render), JavaScript clocks in the frame's time zone, and a real-time toggle for scenes that want
to run faster than one frame a second.
* **Preview on frame** temporarily shows the scene on the actual frame without saving or
deploying it. Good for checking how a colour dithers on real e-ink.
## How to learn [#how-to-learn]
FrameOS ships with many sample scenes. The fastest way to learn is to install them, open them in
the editor, and poke around.
When the visual editor isn't enough, write your own apps - in Nim or JavaScript.
[Editing apps](/guide/apps) is next.
# Useful settings (/guide/settings)
A tour of the most useful options under a frame's **Settings** tab. Unless noted otherwise, each
one is available in the [self-hosted backend](/guide/backend), in [FrameOS Cloud](/guide/cloud)
and on a [standalone frame's](/guide/standalone) admin page - the cloud's
[settings page](/guide/cloud#managing-a-frame) lists the exact set it accepts.
## Frame access [#frame-access]
Each frame's HTTP server (port `8787`) has three access modes - **private**, **protected**, and
**public** - controlling who needs the access key to view or control the frame. See
[Controlling the frame](/guide/control) for details.
## QR control code [#qr-control-code]
By default the frame overlays its control URL as a QR code on the display. You can position it,
restyle it, or turn it off entirely.
## Assets [#assets]
Downloaded images (OpenAI, Unsplash, galleries…) are saved to `/srv/assets` on the frame's SD card
when there's enough free space. This avoids re-downloading (and re-paying for) the same images
after a reboot. Browse, upload, rename and delete them under the frame's **Assets** tab, reuse
them with the **Local image** app, or point a `path` state field at a file or folder so the
control page gets a picker.
This works the same way on [ESP32 boards with a TF card slot](/guide/esp32#assets-and-the-sd-card).
Images too large to decode in memory also spool here on their way to the screen - see
[Big images on small devices](/guide/images).
## Power and battery [#power-and-battery]
Frames that can sleep - the [ESP32-S3](/guide/esp32#power) and the
[Pico W boards](/guide/pico#battery-mode) - get a **Power** section: deep sleep between refreshes,
sleeping only while on battery, aligning wake-ups to wall-clock boundaries, waking periodically to
pick up queued commands, and battery voltage sensing (the ADC pin, the divider ratio and, on
boards that switch the divider, its enable pin - the hardware presets fill these in). A sleeping
ESP32 also wakes on any of its GPIO buttons, and the frame list says **asleep · wakes in 5 min**
while it's down. A Raspberry Pi is always awake and always wall-powered, so it has none of this.
## Nightly reboot [#nightly-reboot]
Frames reboot nightly by default - cheap insurance against memory leaks on a device meant to run
for years. To anyone watching, it looks like a regular re-render. You can disable it or change the
time. A reboot or restart can also be a [schedule entry](/guide/control#scheduling), which is how
ESP32 frames do it.
## Metrics [#metrics]
Frames sample CPU, memory, temperature and disk and ship them to the backend - or to the cloud,
if you switched that on for the frame - every 60 seconds.
Change the interval under **Metrics**, or set it to `0` to turn the sampler off entirely. Changes
apply without a restart.
## Time zone [#time-zone]
Set the frame's time zone so schedules, clocks and calendars line up: the on-device scheduler
fires in the frame's zone, and so do clocks in JavaScript apps and the weather forecast. Pick it
under **Device settings → Timezone** (IANA names like `Europe/Brussels`); on Raspberry Pi OS the
first option, **Detect from frame**, keeps whatever the OS already has. The backend's
**Settings → Default timezone** is what a new frame starts with, and an SD image built in the
backend or the cloud carries the zone with it. Time sync itself works out of the box.
In [FrameOS Cloud](/guide/cloud) every way of adding a frame - SD image, ESP32 flasher, claim
code - seeds the new frame's zone from the browser you used, so a frame comes up in local time
without a visit to its settings.
An [ESP32](/guide/esp32) keeps a zone too: it stores the name plus a few-kilobyte slice of time
zone data for that one zone, so daylight saving switches correctly without a full database on the
chip. It takes the zone from the backend or the cloud, or from `set time_zone Europe/Brussels` on
the serial console. Needs firmware 2026.8.34 or newer (2026.8.39 for JavaScript `Date` to follow
it too); older firmware runs on UTC. If the zone
arrives without its data slice (the console command, or an older backend), the frame fetches the
slice once from `tz.frameos.net` on its next online render pass and stays on UTC until then - worth
knowing on a locked-down network, where SD images also poll `networkcheck.frameos.net` on boot.
## HTTP response size [#http-response-size]
**Maximum HTTP response size for apps** caps how much any single download - an image, a calendar
feed, an API reply - may carry. A response that announces a bigger size is refused before the first
byte; a stream that grows past it is cut off. The default is 64 MiB on a Pi and **4 MiB on an
ESP32**, where it's read at boot (saving it reboots the frame). Raise it for bigger sources. On an
ESP32 a baseline JPEG or PNG decodes straight off the socket and never needs to be held whole;
anything that does - a progressive JPEG, a large feed - spills to the SD card when there is one,
so a 20 MB gallery photo needs nothing but a higher limit. See
[Big images on small devices](/guide/images).
## HTTPS [#https]
Switch on **HTTPS API** and the frame serves its web app and API over TLS on port `8443`, next to
plain HTTP on `8787` - or instead of it, with **Expose only HTTPS port**. From FrameOS 2026.9.14
the frame does this itself on every Linux frame, SD-image frames included; the Caddy proxy that
older releases installed is gone, and the first full deploy after the upgrade switches it off. If
the certificate can't be loaded or the port is taken, the frame logs `tls:config_error` or
`tls:start_error` and stays reachable over plain HTTP.
From 2026.9.15, port and HTTPS changes saved on the frame's own admin page take effect on save: the
page follows the frame to its new address, and a port that is already in use refuses the save
instead of locking you out. A time zone or network share changed there is applied too, no deploy
needed.
## Debug logging [#debug-logging]
Enable debug logging to get per-app timing in the logs - useful to find out which app slows down
your renders.
## SSH keys [#ssh-keys]
Both the [backend](/guide/backend) and the [cloud](/guide/cloud) keep a list of SSH public keys
under **Settings → SSH keys** in the backend and **Account → Settings** in the cloud (the backend
can also generate a keypair for you; the cloud stores public halves only). Mark the ones that should go onto every new frame, and the SD image builders
and the frame's settings page offer them as checkboxes - a key on the card lands in root's
`authorized_keys` on first boot, next to whatever root password you chose.
The other way round, the backend pins a frame's SSH host key the first time it connects, and
refuses a different machine at that address. Replaced the Pi or re-flashed the card? **Forget host
key** in the frame's settings resets it (there is one for the build host too).
## Builds (self-hosted backend only) [#builds-self-hosted-backend-only]
FrameOS is distributed as released binaries, and a normal deploy compiles nothing. The backend's
Settings page keeps the old build machinery under **Advanced: legacy source builds**, collapsed
behind a **Show** link: local Docker cross-compilation, a remote SSH build host, or building on the
device itself. A source build only happens for a [legacy compiled scene](/guide/scenes#execution),
for a frame whose installation mode you set to **Build from source** by hand, or for a distro
outside the release matrix. Releases cover Debian / Raspberry Pi OS bookworm and trixie and Ubuntu
24.04 and 26.04, so a frame still on bullseye builds from source until you upgrade its OS. The
Home Assistant add-on has no Docker socket, so it stays on precompiled binaries unless you point
it at a build host.
# A standalone frame (/guide/standalone)
The simplest way to run FrameOS is to run **only the frame**. No backend, no account: a Raspberry
Pi boots a FrameOS SD image, you connect it to WiFi from your phone, and from then on you manage
it through the web app the frame serves itself. This is the right starting point if you want one
frame, want it quickly, and want nothing else in the picture.
If you later want more - several frames in one place, compiled Nim apps, a build server - you can
hand the same frame to a [self-hosted backend](/guide/backend) (**Adopt frame**) or to
[FrameOS Cloud](/guide/cloud) (link it with the code on its display). Nothing needs reflashing.
A standalone frame is a Raspberry Pi thing. An [ESP32](/guide/esp32) has no admin page of its
own, so it is set up from a backend or from the cloud - though it renders on its own afterwards
all the same.
## 1. Flash a generic image [#1-flash-a-generic-image]
Download the image for your board from the
[latest release](https://github.com/FrameOS/frameos/releases):
| Image | Boards |
| ----------------- | ----------------------------- |
| `raspberry-pi-64` | Zero 2 W, Pi 3, Pi 4, CM3/CM4 |
| `raspberry-pi-5` | Pi 5, CM5 |
| `raspberry-pi-32` | Zero, Zero W, Pi 1, CM1 |
Flash it with [Raspberry Pi Imager](https://www.raspberrypi.com/software/) ("Use custom image")
or [balenaEtcher](https://etcher.balena.io/) - both read the `.img.gz` as is. Connect the display,
insert the card, power up.
A Pi on Raspberry Pi OS, or any Debian (bookworm, trixie) or Ubuntu (24.04, 26.04) machine on
arm64, armhf, armv6 or amd64, can skip the image. Run
`curl -fsSL https://cloud.frameos.net/install.sh | sudo sh` on the device: it asks for the
display and an admin password, downloads the latest release, checks its signature against the FrameOS release key, installs it
as a systemd service, and starts the
frame standalone. Continue at [step 3](#3-open-the-admin-page).
## 2. Set it up from your phone [#2-set-it-up-from-your-phone]
With no WiFi configured, the frame starts a hotspot called **`FrameOS-Setup`** (password
`frame1234`). Join it from your phone or laptop and open `http://10.42.0.1:8787/` (the panel
shows the same address). The setup page asks
for:
* your **WiFi** network and password;
* the **display** - every driver is in the image, so you pick your panel here, plus rotation and
any driver extras (VCOM for an IT8951 panel, for instance);
* a **name** and an **admin password** for the frame;
* the **time zone** - preselected from your phone (2026.9.10+);
* **Manage via**: choose **Nothing (standalone)**. The other two options enroll the frame with
[FrameOS Cloud](/guide/cloud) or point it at a [self-hosted backend](/guide/backend) - you can
change your mind later from the admin page.
Save, and the Pi joins your network: the "Saved!" page names the network it is joining, waits for
the frame to show up on your LAN and opens it there. If the WiFi password was wrong, the frame keeps trying for
about 90 seconds before the `FrameOS-Setup` hotspot comes back. On HDMI you watch it happen: the FrameOS status screen draws
the boot steps as they go ("Checking network…", "Network connected. Loading scenes…"). E-ink
panels get the same screen once the frame is up: its name, display and resolution, IP address and
whether the internet is reachable, a clock and time zone, how it is managed (*standalone*), and the
frame's URL - as `.local` and as a plain IP, plus a `/?k=…` access link while the frame
has no admin login yet. If the frame gets an
address but nothing resolves, the log carries a `networkCheck:diagnostics` snapshot (DNS, routes,
the DHCP lease) that says why.
## 3. Open the admin page [#3-open-the-admin-page]
Browse to `http://:8787/admin` (the status screen shows the address) and sign in with
the admin password. This is a full management UI served by the frame itself:
* **Scenes**: install from the [scene store](/guide/scene-store), built into the **Add scene**
drawer (the bundled samples stand in when the store can't be reached), upload a scene ZIP, or build your own in the same
node-graph [scene editor](/guide/scenes) the backend has. Scenes are interpreted, so there is nothing to
compile - save, and it is live.
* **Settings**: everything under [Useful settings](/guide/settings) - access mode, QR code, time
zone, nightly reboot, metrics, the display driver.
* **Schedule**, **Logs**, **Metrics**, **Assets** - the same panels a backend shows for the frame,
running against the device.
* **Settings → FrameOS upgrade**: the frame downloads a new release itself, verifies the
signature, swaps it in and restarts - rebooting only when the new release needs it.
The control page at `http://:8787/c` and the [HTTP API](/guide/control) work exactly as
on any other frame, and the QR code on the display points at them.
## What you give up [#what-you-give-up]
Only what needs a build machine or a second computer: [compiled scenes and custom Nim apps](/guide/apps#nim-apps),
the backend's **Terminal**, headless Chromium screenshots, managing several frames from one
screen, and the backend's library of scene templates and fonts. JavaScript apps, code nodes,
state fields, all \~40 built-in apps, schedules and images of any size are all there.
To change the display later without the web UI, the FrameOS binary has a small CLI you can run over
SSH, if you enabled root login:
```bash
cd /srv/frameos/current
./frameos set-display --device=pimoroni.inky_impression_13 --rotate=90
./frameos driver-setup
```
## Next step [#next-step]
Want to see a few more frames at once, or build apps in Nim? [Install the backend](/guide/backend)
and adopt this frame. Want to manage it from anywhere without hosting anything?
[Link it to FrameOS Cloud](/guide/cloud#link-a-frame-that-already-runs).
# Virtual frames (/guide/virtual)
A **virtual frame** is a frame with no device behind it. The backend renders its scenes and
serves the result two ways:
* **An image URL** - one PNG per request, at whatever size and colour mode you picked.
* **A self-refreshing page** - a black, full-bleed kiosk page that swaps the image in place on
the frame's interval, without the white flash a meta-refresh would cause.
Both URLs appear in the frame's settings once it is added.
Virtual frames exist only in the [self-hosted backend](/guide/backend), since it is the backend
that renders them. [FrameOS Cloud](/guide/cloud) never renders on your behalf, so it has no
virtual frames.
## What it's for [#what-its-for]
* **Old tablets, browser kiosks and signage players.** Anything that can show a web page becomes
a frame - no FrameOS software on the device at all.
* **Trying FrameOS**, or designing scenes, before any hardware arrives.
* **Feeding other software**: a Home Assistant dashboard card, a Slack unfurl, a wallpaper
script, an e-ink display driven by something else entirely - anything that can fetch a PNG.
* **Previewing what a panel will look like.** Pick the colour mode of the panel you're
considering and the render is quantised the same way, dithering included.
## Add one [#add-one]
Click **New frame → Virtual frame**, then set:
* **Width and height** in pixels.
* **Colour mode**: full colour, black & white, 4-level grayscale, black/white/yellow/red,
7-colour ACeP, or Spectra 6. Anything but full colour is dithered to that palette, exactly as a
real panel would be.
From there it's an ordinary frame: install scenes, edit them in the
[scene editor](/guide/scenes), set a render interval, change its state fields live.
## How it differs from a real frame [#how-it-differs-from-a-real-frame]
* **The backend does the rendering**, so a virtual frame stops updating when the backend does.
Every other kind of frame keeps going.
* **Assets and scene state live on the backend** rather than on a device.
* **Anything tied to real hardware** - GPIO buttons, the frame's own web server and QR code,
panel-specific behaviour - has nothing to talk to.
Everything else works: the same apps, the same scenes, the same state fields.
## Sharing the URLs [#sharing-the-urls]
The image and page URLs carry the frame's access key as a `?k=` parameter, and the page never
prints the key in its markup - only inside the image URL it already arrived with. Anyone with the
link can see the frame, so treat it like any other unlisted URL: fine for a tablet on your wall,
not something to post publicly.
# 3D-printed cases (/cases)
A frame isn't finished until it stops looking like electronics. The **FrameOS Case Maker** at
[cases.frameos.net](https://cases.frameos.net) generates a parametric, 3D-printable case around
your exact panel and Raspberry Pi - in the browser, powered by OpenSCAD.
[
](https://cases.frameos.net)
Pick your panel model and Pi, then customise:
* case depth and bezel dimensions
* print-in-place **kickstands**
* wall-mounting holes
* USB power cutouts
* screw posts and heat-set insert holes
* SD card access, side buttons, Pi cooling
…then download the STL and print it.
The case designs are open source at
[github.com/FrameOS/cases](https://github.com/FrameOS/cases) - PRs with new panel dimensions are
very welcome. For a full build walkthrough (13.3" and 7.3" Spectra panels, slim builds, USB-C
ports, heat-set inserts), watch the
[e-ink build video](/blog/eink-spectra-waveshare-pimoroni).