# 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: The same image on a Spectra 6 panel (left) and an ACeP panel (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. Editing an app 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. The FrameOS backend 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. Control via QR code ## 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: The on-device admin panel, Settings tab * **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. Adding a frame ## 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. Adding scenes ## 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. Deploy finishing The image on the physical frame ## 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. Scene control fields 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. Frames around the house ## 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. Raspberry Pi Imager OS selection 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. SSH key settings in the imager 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. Creating a new scene ## 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: Adding a render app Then drag its **image** field out and pick a green data app to feed it - use **FrameOS Gallery** if unsure. Press deploy: Adding a data app ## 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. Cache controls ## 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). Adding a state field Editing state live 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.) Code nodes ## 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. Sample scenes in the editor 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. [The FrameOS Case Maker](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. Printed cases with kickstands 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).