PicoReader is an EPUB reader for the Anbernic RG XX devices running muOS. It's built from scratch in Python with no external dependencies — no PIL/Pillow, no pip installs, nothing to set up. Just copy it on and start reading.
Image decoding uses the device's own native libSDL2_image/libjpeg
libraries for speed, but that's never a hard requirement — a complete,
pure-Python JPEG decoder is built in as an automatic fallback. If a
future muOS update ever changes or removes those native libraries,
PicoReader keeps working on its own rather than breaking.
It's a companion app to Pico8FavsSorter, another muOS app for the same device.
The CubeXX-H's square 720×720 screen wasn't an accident of this project — it's a deliberate design choice by the device itself, and part of why it was bought in the first place. It was picked for running PICO-8 games (which are natively square), with minimalism as the whole point. The ebook reader started as a smaller side idea on the same hardware and ended up becoming the main passion project.
Library and Reader screens, captured directly from PicoReader's real rendering code (headless SDL2, real bundled font, real books) — not mockups.
Default · Dim Warm · Deep Amber · Red Shift · Adventure
- Grab
PicoReader.muxappfrom Releases. - Install it like any other
.muxappthrough muOS's Archive Manager — or unzip it directly into/run/muos/storage/application/so you end up with/run/muos/storage/application/PicoReader/. - Copy your
.epubfiles into PicoReader's library folder. - Launch PicoReader from the Applications menu and start reading.
- Reads real-world EPUBs, including ones with complex formatting, footnotes, and internal links — not just simple text files.
- Shows images, including progressive JPEGs, using a JPEG decoder
built from scratch for this project (there's no PIL/Pillow on-device
to do this for us), with a much faster native decode path used
automatically when the device has
libSDL2_imageavailable. - Adjustable text size that applies everywhere — the book text, menus, and every list on screen — not just the reading area.
- Bookmarks and "resume reading" that return you to the exact spot you left off, down to the paragraph.
- A full table of contents / chapter browser, plus quick next/previous chapter buttons.
- Library organization — pin favorites, mark books Finished/ Unfinished with a filter to match, sort by title/author/last read/ recently added, and a "Continue Reading" shortcut that jumps straight into your most recently read book. Real folder navigation (New/Move/ Delete Folder, multi-select batch move) lives together in one "Edit Library" submenu, kept separate from the everyday sort/filter/pin actions so the main menu stays short. There's also an optional "Open Last Book on Launch" toggle if you'd rather skip the Library screen entirely and land right back in your book on startup.
- A built-in image cache you can manage from the Settings screen, including a RAM-only mode if you'd rather not write to disk at all.
- 5 selectable themes, including three progressively warmer/redder options (Dim Warm, Deep Amber, Red Shift) meant for reading right before bed — less blue light, easier on melatonin/night vision. Switch anytime from the Library menu.
- Optional book downloaders — browse and download books right on the device (see below).
- A boot splash screen — a short, skippable intro (START/A/B skip straight past it) before landing in your Library or last-read book.
- Graceful handling of unusually large pages — a few real-world EPUBs have individual pages that are genuinely huge (footnote-heavy reference works, long daily-reading schedules — one real example weighs in at 4.5 million characters on a single page). PicoReader builds these progressively in the background, a chunk at a time, so the page is already readable and scrollable while the rest keeps building behind it — no long blocking wait, no frozen screen. The finished result is cached so every future visit is instant, and you can back out or jump chapters mid-build at any time.
- Built with reliability in mind — EPUB parsing is hardened against malformed or maliciously-crafted files (a single bad book can't crash the reader, corrupt your library, or write outside its own folder), and every on-screen/on-disk cache is bounded in size, so long reading sessions stay within the device's memory rather than growing unbounded over time.
Every screen shows its own control hints at the bottom, so you're never guessing. The basics:
| Button | While reading | In the Library |
|---|---|---|
| D-PAD | Scroll / move between links | Move selection (LEFT/RIGHT jump 10) |
| A | Follow a link / confirm | Open the selected book |
| B | Go back | Quit |
| L / R | Previous / next page | Font size smaller / larger |
| L2 / R2 | Previous / next chapter | Open downloader (if installed) |
| Y | Toggle fast-scroll | Change sort order |
| X | Open the menu | Open the Library menu |
| START | Add a bookmark here | Pin a book |
| SELECT | -- | Mark the book Finished / Unfinished |
Book delete lives in the Library menu (X → Delete Book) rather than on a bare button — it always targets whichever book was highlighted when you opened the menu, and needs a second press to confirm. The Library menu also has sort/filter shortcuts, theme switching, and the Settings screen (image cache, RAM-only mode, Continue Reading / Open Last Book on Launch, and more).
PicoReader can browse and download books without needing a computer. Two sources are included out of the box:
gutenberg_fetch.pypulls public-domain books directly from Project Gutenberg's own official OPDS catalog feed — no third-party API in between.librivox_fetch.pystreams and downloads free public-domain audiobooks from LibriVox, browsed through 145 real genre categories (a proper nested drill-down, not a flat list) or by search. Streaming is the default action, with a full-book download available too.
Every category — from either source — is always browsed live; nothing is ever served from a stale local snapshot, so what you see is always what's really there right now. The one exception is deliberate: a "Downloaded Audiobooks" entry always sits at the top of LibriVox's category list, showing exactly what you've already downloaded (grouped by book, so tracks never get mixed between titles), fully browsable offline. Anything downloaded — a whole audiobook or a single track — can be deleted right from there.
Want to add your own source? See PLUGIN_TEMPLATE.py for the plugin
contract — it's a small, self-contained interface.
- An Anbernic RG CubeXX-H, or another muOS device with Python 3 and SDL2/SDL2_ttf available.
PicoReader is built and tested primarily on the RG CubeXX-H (720×720). Starting with v0.1.148 it also runs on other muOS screen sizes without any layout changes, by detecting the real screen at boot and having SDL2 scale the app's fixed 720×720 canvas to fit — centered, with black bars on the sides rather than stretching. Confirmed device groupings (via community.muos.dev): 640×480 (RG28XX, RG35XX/+/2024/H/SP, RG40XX H/V), 720×480 (RG34XX family), 720×720 (RGCubeXX — primary target), 1024×768 (TrimUI Brick), 1280×720 (TrimUI Smart Pro).
Device status:
| Device | Status |
|---|---|
| RG CubeXX-H (720×720) | Confirmed working — primary dev/target device |
| RG34XX-SP (720×480) | Confirmed working — boots, button mapping, and 720×480 canvas rendering all verified on real hardware |
| All other listed resolutions | Verified through headless simulation only (real SDL2, no exceptions, correct scale factors confirmed via SDL's own API) — not yet confirmed on real hardware |
The scaling approach follows a standard, well-documented SDL2 pattern
(SDL_RenderSetLogicalSize) and avoids a specific real-world bug found
in another muOS app during this work (non-uniform X/Y scaling that
stretches the UI instead of preserving aspect ratio), but for the
devices not yet confirmed, "should work" is not the same claim as
"confirmed working." The CubeXX-H path itself is completely unaffected
by this change (separate code branch, zero risk of regression there).
If you test this on another device and it looks right — or doesn't —
please open an issue or PR.
The app is just the contents of this repo. Stage everything inside a
folder named PicoReader/ (main.py, epub_engine.py, mini_jpeg.py,
native_image.py, native_media.py, gutenberg_fetch.py,
librivox_fetch.py, mux_launch.sh, assets/, README.md,
LICENSE.md), then zip from one level above that folder so
PicoReader/ itself is the zip's top-level entry:
zip -r PicoReader.muxapp PicoReader/
Don't zip from inside the staging folder (e.g. zip -r ../out.muxapp .)
— that omits the wrapping folder, and muOS's unpacker will dump the
files straight into applications/ instead of applications/PicoReader/,
which breaks the install silently (the app just won't launch, with no
clear error). You can sanity-check any .muxapp before installing with
unzip -l PicoReader.muxapp — every path listed should start with
PicoReader/. Name the finished file PicoReader.muxapp and install it
the same way as a downloaded release.
| File | What it does |
|---|---|
main.py |
The app itself — UI, controls, state, image handling |
epub_engine.py |
Parses EPUB files (no external libraries needed) |
mini_jpeg.py |
Decodes JPEG images, including progressive JPEGs, in pure Python |
native_image.py |
Optional ctypes bridge to the device's own libSDL2_image for much faster decoding of JPEG, PNG, and other formats — used automatically when available, with mini_jpeg.py as the automatic JPEG-only fallback |
native_media.py |
ctypes bridge to the device's own mpv/ffplay for audio/video streaming and playback — what LibriVox's audiobook streaming runs on |
mux_launch.sh |
Tells muOS how to launch the app |
gutenberg_fetch.py |
The built-in Project Gutenberg downloader |
librivox_fetch.py |
The built-in LibriVox audiobook downloader/streamer |
PLUGIN_TEMPLATE.py |
Starting point for writing your own downloader |
assets/ |
Bundled fonts (see License below) |
main.py keeps its own changelog and architecture notes at the top of
the file, for anyone — human or AI — picking the project back up later.
This project's own code is MIT licensed. The bundled DejaVu Sans
Condensed fonts are third-party and stay under a permissive
Bitstream Vera-derived license. Both full license texts are in
LICENSE.md.
I tested this app extensively with the amazingly designed and complex EPUBs published by JW.org, and it handles them incredibly well. EPUBs of this kind are some of the most complex I've come across, and until building this reader I hadn't found a satisfactory way to read them anywhere outside of Apple's iBooks.
I used Claude Code as a tool throughout development, but the UI, the overall design philosophy, and the plugin architecture were designed by me.
- DejaVu Fonts — public domain changes over a Bitstream Vera base, permissively licensed
- Built for muOS on the Anbernic RG CubeXX-H






