A gamepad-native web browser for unconventional devices.
retsurf (retro + surfing) is an experimental web browser written in Rust, built to bring a fully featured web experience to devices where traditional browsers aren't practical.
Web rendering comes from Servo, with SDL2 handling windowing and input and egui providing the UI. retsurf runs without X11 or Wayland, rendering OpenGL ES directly through KMSDRM, and is designed around gamepad-first navigation.
It runs on PortMaster-compatible Linux handhelds, including the Miyoo Mini family, as well as regular desktops and Android. On GPU-less devices such as the Miyoo Mini, retsurf uses CPU rasterization for both web pages and its UI.
Work in progress. Early development — expect bugs.
Handheld Linux devices have no good browser options. Lightweight browsers often struggle with modern, JavaScript-heavy sites, while desktop browsers depend on a windowing system, mouse and keyboard, and hardware that these devices do not have.
retsurf is an attempt to fill that gap: a modern web engine, gamepad-first controls, and direct rendering without a compositor.
-
Gamepad-native navigation
Virtual cursor with stick/D-pad control, Vimium-style link hints, and an on-screen keyboard (QWERTY + ЙЦУКЕН). -
Customizable controls
Every gesture is rebindable in-app or inbindings.toml, with D-pad scrolling for stickless devices. -
Tabs, bookmarks, history, and downloads
Everything lives in one full-screen menu. Downloads run in the background with progress and cancellation, with an ⬇ toolbar chip for active downloads. -
Real page zoom
Reflows the layout rather than magnifying it, following Firefox's 50–300% zoom ladder. Zoom is per-tab, so the whole web remains usable on a small screen. -
Reader mode
Strips pages down to their articles using Mozilla's Readability. Runs in place, so it also works with logged-in and dynamically rendered pages. -
Dark web pages
Uses sites' own dark themes throughprefers-color-scheme, or forces a dark appearance by inverting pages that don't provide one. -
Ad & tracker blocking
Network-level blocking powered by Brave'sadblock-rust, using EasyList and EasyPrivacy. Filters are compiled and cached locally, making warm starts instant and allowing blocking to work offline. -
Native start page
A search/URL field over a speed-dial grid of pins (retsurf:home), fully controller-navigable like every other overlay. -
In-app updates
Checks GitHub for updates and shows release notes inline. On PortMaster handhelds and Linux desktops, updates can be installed in place; elsewhere, the release page is opened. Supports stable, beta, and dev channels. -
Web Audio
Custom Servo media backend with SDL2 output. Supports oscillators, gain, filters, panners, scripted buffers, anddecodeAudioDatafor MP3, WAV, FLAC, Ogg/Vorbis, and AAC/M4A, with resampling to the context rate. -
Modern rendering
Servo's WebRender uses OpenGL ES 3.x with a single GL context and zero CPU readback, drawing directly into the on-screen framebuffer.
You need Servo's build dependencies. On Debian/Ubuntu:
sudo apt-get install -y build-essential clang cmake curl git gperf pkg-config python3 \ libssl-dev libdbus-1-dev libfreetype6-dev libglib2.0-dev \ libgl1-mesa-dev libegl1-mesa-dev libgles2-mesa-dev \ libharfbuzz-dev liblzma-dev libudev-dev libunwind-dev libsdl2-dev
Then:
cargo run
On a Wayland desktop, retsurf auto-selects SDL's Wayland driver and a GLES context. Environment variables override the config, data, and download paths and set logging — see Configuration.
retsurf also builds an APK: SDL2 loads the Rust code as a cdylib and the GLES render path carries over, with touch input and the system soft keyboard. With the Android SDK/NDK installed:
rustup target add aarch64-linux-android
cargo install cargo-ndk --locked
./android/scripts/build.sh release # android/app/build/outputs/apk/release/app-release.apk
adb install -r android/app/build/outputs/apk/release/app-release.apkThere is no GPU on this device, so the page is rasterized by WebRender's software
backend and the chrome by SDL's 2D renderer — the software cargo feature. The armv7
binary and the card layout around it are cross-built in a container:
tools/armhf/build.sh # prints the binary's path tools/armhf/package-allium.sh -n # dist/retsurf-allium.zip around what was just built
Packaging borrows three sets of files it cannot ship itself — the Miyoo SDL2 build above all; the script header says where each comes from. See the Allium package for the device side: install, controls, and what 128 MB of RAM costs you.
config.toml (settings) and bindings.toml (gamepad/keyboard mappings) live in the
user data dir (SDL_GetPrefPath, e.g. ~/.local/share/mxmgorin/retsurf/ on Linux).
Templates with the defaults are written on first run, and most settings are editable
in-app from the settings overlay.
See Configuration & bindings for every option and the full bindings reference.
Bug reports and ideas are welcome — open an issue for anything broken or missing. If you find retsurf useful, a star on GitHub helps others discover it — and keeps me motivated.
- Handheld notes — how it works, architecture, porting status
- Android notes — build/packaging, storage, touch, lifecycle, status
- Allium package — the Miyoo Mini build: install, controls, limits
- The Servo Book — the embedded engine: architecture, concepts, build system