One toolkit. Every tweak your handheld was missing. A fast, beautiful plugin platform for Linux gaming handhelds — an in-game overlay in Gaming Mode, a standalone app on the desktop — built end-to-end in TypeScript.
Tune your handheld without ever leaving your game. Tap the overlay, dial in your TDP, set a custom fan curve, check if a game runs well on Proton, launch your Epic library, install a recompiled N64 classic — then get straight back to playing. Twenty-plus plugins, one slick d-pad-friendly UI.
Works in both Gaming Mode and Desktop Mode. In Steam's Gaming Mode it runs as an in-game overlay layered over your game; on the desktop it runs as a standalone app in its own window. Loadout detects which mode you're in and adapts automatically.
Install → · Plugins · Supported devices · Build from source
🚧 Status: pre-launch. Developed and tested on OneXPlayer devices and Valve's Steam Deck — and I'm looking for testers on other handhelds.
The greatest hits — and there are a dozen more below:
- 🕹️ RecompHub — Browse, install, and play natively-recompiled classics (Zelda 64, Mario 64, and friends). No emulator, one tap to play.
- 🛒 Store Bridge — Your Epic Games library, installed and launchable right alongside your Steam games.
- 🖼️ SteamGridDB — Gorgeous custom box art, heroes, and logos for every game in your library.
- ⚡ LSFG-VK — Lossless Scaling frame generation as a Vulkan layer — more FPS, one toggle.
- 🟢 ProtonDB Badges — "Will it run?" answered at a glance with a compatibility tier on every game tile.
- 🔋 TDP & Fan Control — Find your perfect battery-vs-performance balance with presets and live curves.
- 🎨 Theme Loader & Sound Loader — Reskin Big Picture with community CSS themes and UI sound packs.
- ⏱️ HowLongToBeat & PlayTime — Know what you're signing up for, and where your hours actually go.
Loadout is the same app in both of Steam's environments — it detects which one you're in (it looks for Gamescope) and adapts:
- Gaming Mode → in-game overlay. A CEF window layered over Gamescope via X11 atoms. Tap it open, tweak, and get back to your game without leaving it.
- Desktop Mode → standalone app. It runs as an ordinary window on your desktop — no game underneath to freeze, controller input still grabbed so it stays d-pad-friendly.
A handful of plugins that reach into Steam's Big Picture UI (badges, CSS theming) are Gaming-Mode-only by nature; everything else works in either mode.
In Gaming Mode, plugins reach right into Steam's Big Picture UI — themed, badged, and native:
RecompHub turns recompiled classics into native Steam entries with full artwork — a dedicated collection in your library:
Theme Loader restyles the whole interface with community CSS themes:
Loadout is Linux-only by design — it targets distros that ship Steam Gaming Mode. No Windows or macOS build.
| OS | Status |
|---|---|
| SteamOS (Steam Deck) | ✅ Supported |
| Bazzite | ✅ Supported |
| CachyOS | ✅ Supported |
Hardware tested so far:
| Device | Status |
|---|---|
| OneXPlayer APEX | ✅ Daily-driven |
| Steam Deck | ✅ Tested |
| OneXPlayer F1 Pro | ✅ Tested |
🙋 Got a different handheld? ROG Ally, Legion Go, OneXPlayer, GPD, AYANEO, AOKZOE — I'd love your help. I'm actively looking for testers on other devices. Open an issue with your hardware and let's get Loadout running on it.
See docs/os-compatibility.md for per-distro notes (including the bundled-glibc caveat on stock SteamOS).
One command — downloads the prebuilt binary + overlay, verifies SHA-256, and
sets up the services. The backend installs as a system service (one sudo
prompt at install) so plugins can touch hardware without prompting you again;
the overlay runs as your user:
curl -fsSL https://raw.githubusercontent.com/srsholmes/loadout/main/scripts/install.sh | shThen open Loadout — as an overlay from Gaming Mode, or as a standalone app on the desktop — and you're set. To remove it:
curl -fsSL https://raw.githubusercontent.com/srsholmes/loadout/main/scripts/uninstall.sh | shDetails & requirements
The installer downloads the prebuilt loadout binary + Electrobun overlay
into ~/.local/share/loadout/ and ~/.local/share/loadout-overlay/, verifies
SHA-256 against the release's SHA256SUMS, and writes the systemd units: a
system loadout.service (the backend, runs as root — hence the one-time
sudo) and a user loadout-overlay.service. See
docs/install-locations.md for exact paths.
Runtime requirements: an X11/Xwayland display, membership in the input group
(so the overlay can grab evdev devices), and a working Steam install. The CEF
libraries ship inside the overlay archive.
On SteamOS the installer additionally builds a small libwebkit2gtk-4.1
library closure (the overlay's native wrapper dlopens it) from a Fedora
container via podman — shipped in SteamOS Holo 3.7+ — the first time, then
caches it. This is kept out of the download on purpose so the release stays
small; Bazzite/CachyOS/Fedora already provide these libraries and skip the step
entirely.
If this repository is private, the install command will 404 until it's made public;
install.shhonours aGITHUB_TOKENenv var for authenticated installs.
In Gaming Mode, trigger your configured wake shortcut on the controller to
bring up the overlay. On the desktop, launch Loadout like any other app and
it opens as a standalone window. In either mode, if you're on a keyboard (or
your controller shortcut isn't working yet), Ctrl+4 toggles it open and
closed at any time — handy as a fallback during first-time setup.
First-time setup is much smoother with a keyboard attached. You'll be entering a few values (like a SteamGridDB API key) and configuring shortcuts, and typing those on a keyboard is far quicker than the on-screen keyboard. Once you're set up, day-to-day use is fully controller- and d-pad-friendly.
It's highly encouraged. The SteamGridDB plugin — and the custom artwork that other plugins pull in (box art, heroes, logos) — needs a free API key to fetch art. Grab one from steamgriddb.com/profile/preferences/api and paste it into the SteamGridDB plugin's settings; you only enter it once and it persists across reinstalls.
Yes — that's the main way. In Settings → Controller you can bind the overlay
to Guide + B or Guide + X. (Guide + A and Guide + Y are deliberately left
alone — Steam and InputPlumber reserve them for the QAM and Steam menu.) Ctrl+4
on a keyboard always works too, as a fallback.
Re-run the same install command — it's idempotent. It checks for a newer binary and overlay, refreshes the bundled plugins, and leaves your config untouched:
curl -fsSL https://raw.githubusercontent.com/srsholmes/loadout/main/scripts/install.sh | shAll 20-plus plugins ship bundled with the release — there's nothing extra to download. Enable or disable individual plugins from Settings → Plugins.
While the overlay is open, it exclusively grabs your controller so your inputs drive the overlay and don't leak through to the game underneath. That's by design — close the overlay and the controller returns to Steam immediately. If a pad stays unresponsive while the overlay is hidden, make sure you're on the latest version.
Everything — theme, UI scale, favourites, home layout, controller shortcuts, and
per-plugin state — lives in ~/.config/loadout/config.json (honouring
$XDG_CONFIG_HOME) and survives reinstalls and CEF profile wipes.
Backend logs are at ~/.config/loadout/logs/loadout.log, or follow them live:
journalctl -u loadout -f # backend (system service)
journalctl --user -u loadout-overlay -f # overlay (user service)Open an issue with your handheld, distro, and steps to reproduce — and if you're on an untested device, I'd love to hear from you.
curl -fsSL https://raw.githubusercontent.com/srsholmes/loadout/main/scripts/uninstall.sh | sh- TypeScript end to end. The plugin host, every plugin backend, every plugin UI, and the overlay itself run on Bun. A full plugin — backend + UI — is typically 150–300 lines.
- Our own overlay surface. A standalone CEF window layered over Gamescope via X11 atoms — not an injected panel, so Steam redesigns don't break it. The same window runs as the Desktop Mode app.
- Injection only when you want it. Plugins can reach into Big Picture via CEF's remote-debug protocol (badges, theming) — but it's opt-in, not the default path.
- Batteries-included SDK.
@loadout/uigives plugin authors typed RPC, themed React components, gamepad-aware spatial navigation, and persistent settings out of the box.
Build from source & develop
git clone https://github.com/srsholmes/loadout
cd loadout
bun install
bun run build-and-install # compile + install to ~/.local/share/, enable servicesThe overlay ships a patched Electrobun native wrapper (see
apps/loadout-overlay/vendor/README.md)
that fixes a 100% CPU spin in CEF's browser process. The build and install
scripts swap it in automatically — but electrobun dev does not, so the
dev-mode overlay runs the stock wrapper and pins a CPU core.
Recommended — build and install to see accurate results. This is the only workflow that matches what users actually run: the patched wrapper, the real systemd user units, and production CEF flags. Re-run it after each change:
bun run build-and-install # compile + install + enable services
systemctl --user restart loadout-overlay # restart the overlay to pick up the build
journalctl --user -u loadout-overlay -f # follow overlay logsCEF DevTools are at http://localhost:9222 (attach any Chromium or use CDP).
Fast UI iteration (hot reload). For quick webview/React work where the
native runtime doesn't matter, bun run dev:overlay starts the loader dev
server + Electrobun with live reload:
bun run dev:overlayCaveat: dev mode uses the stock native wrapper, so expect a busy CPU core
and none of the patched-wrapper behaviour. Always confirm a change with
build-and-install before trusting it.
Twenty-plus and counting — every one TypeScript end-to-end. The screenshots below are captured live from the running overlay, so they always match the current build.
Browse, install, and play recompiled retro games natively
Surface your Epic Games library as Steam shortcuts (GOG, Amazon, Ubisoft, xCloud planned)
Browse and apply custom game art (grids, heroes, logos, icons) from SteamGridDB
Browse, install, and toggle community CSS themes for Steam's Big Picture UI
Injects HowLongToBeat completion times into Steam library and store pages
Shows ProtonDB compatibility ratings for your Steam library — tier badges on every game tile, plus per-game detail in the home widget.
Track time spent playing games, including non-Steam games
Install and configure the LSFG-VK Vulkan frame generation layer
- Apex — OneXPlayer Apex device fixes: recover the internal gamepad when its xHCI controller dies on resume (or blacklist the hid-oxp driver to prevent the drop-out), and block the fingerprint reader from waking the device on a light touch.
- Battery Tracker — Battery level, charge rate, estimated time remaining, and charge history
- Bluetooth — Quick connect/disconnect paired Bluetooth devices without leaving the game
- Disable Controller Input — Silence individual controllers by telling InputPlumber to drop their virtual targets — useful for handhelds where the built-in pad is hogging player 1
- Display Settings — Adjust display brightness and saturation
- Fan Control — Monitor and control fan speed, temperature, and fan curve presets
- Flatpak Manager — Manage and update Flatpak applications without leaving your game
- HLTB — Injects HowLongToBeat completion times into Steam library and store pages
- InputPlumber — Install the InputPlumber input-routing daemon — no-op if a system package or a previous run already installed it
- Launch Options — Manage Steam game launch options and presets
- LSFG-VK — Install and configure the LSFG-VK Vulkan frame generation layer
- Network Info — Network information, WiFi details, and Cloudflare speed test
- PlayTime — Track time spent playing games, including non-Steam games
- ProtonDB Badges — Shows ProtonDB compatibility ratings for your Steam library — tier badges on every game tile, plus per-game detail in the home widget.
- Quick Links — Open YouTube guides, ProtonDB, wikis and more for the game you're playing
- RecompHub — Browse, install, and play recompiled retro games natively
- RGB Control — RGB LED control for Linux handhelds — supports OpenRGB, sysfs LEDs, and platform-specific interfaces
- Sound Loader — Browse, install, and toggle community UI sound packs from deckthemes.com
- SteamGridDB — Browse and apply custom game art (grids, heroes, logos, icons) from SteamGridDB
- Storage Cleaner — Shows disk usage, shader cache sizes, and lets users clean up space
- Store Bridge — Surface your Epic Games library as Steam shortcuts (GOG, Amazon, Ubisoft, xCloud planned)
- TDP Control — Adjust CPU/APU TDP wattage with presets and a slider
- Theme Loader — Browse, install, and toggle community CSS themes for Steam's Big Picture UI
A plugin is a directory under plugins/ with a package.json (carrying a
plugin field — the manifest), an optional backend.ts (a PluginBackend
class exposing RPC methods), and an app.tsx UI entry that renders in the
overlay. Plugins that want to reach into Steam's own Big Picture UI (badges,
CSS) drive it from their backend via @loadout/steam-cdp. See
docs/plugin-development.md for the full API.
The overlay is an Electrobun (CEF) app at apps/loadout-overlay/: src/bun/
is the Bun + libc-FFI main process (the evdev read loop, EVIOCGRAB/EVIOCSMASK,
Gamescope X11 atoms, NavController, and the typed RPC surface the webview talks
to); src/webview/ is the CEF UI boot shim that pulls in the shared React tree
from src/overlay/. The Bun loader + CLI lives in apps/loadout/. See
docs/architecture.md and
docs/overlay-gamescope-integration.md.
loadout/
├── apps/
│ ├── loadout/ # Bun loader + CLI: HTTP/WS server, plugin manager, Steam injector
│ └── loadout-overlay/ # Electrobun (CEF) overlay
│ ├── src/bun/ # Bun + libc-FFI main process (evdev, Gamescope atoms, RPC)
│ ├── src/overlay/ # Shared React tree (App, plugin host, sidebar, Settings)
│ └── src/webview/ # CEF UI boot shim
├── packages/
│ ├── types/ # Shared interfaces (PluginBackend, RPC protocol)
│ ├── ui/ # @loadout/ui — React SDK + useBackend + spatial nav
│ ├── exec/ # Subprocess helpers (command allow-list)
│ ├── steam-paths/ # Steam install discovery
│ ├── steam-cdp/ # Chrome DevTools Protocol client (Steam CEF :8080)
│ ├── steam-cef-badges/ # CEF remote-debug injection into Steam Big Picture
│ ├── steam-shortcut/ # Steam shortcut writing
│ ├── vdf/ # Valve VDF parser
│ ├── sgdb-art/ # SteamGridDB artwork fetch
│ ├── game-library/ # Game library helpers
│ ├── per-game-profiles/ # Per-game profile storage
│ ├── plugin-storage/ # Plugin state persistence
│ ├── external-cache/ # XDG cache helpers
│ ├── file-picker/ # File picker
│ └── deck-hid/ # HID device access
├── plugins/ # The plugin suite (one dir per plugin)
├── scripts/ # Build/install + screenshot & README tooling
└── docs/ # Architecture, plugin dev, Steam injection, …
| Command | Description |
|---|---|
bun run dev:overlay |
Loader dev server + Electrobun overlay with hot reload |
bun run build |
Compile loader binary + Electrobun overlay tree |
bun run build-and-install |
build + install into ~/.local/share/ and enable services |
bun run typecheck |
tsc --noEmit |
bun run test |
Backend + UI tests |
bun run lint / bun run format |
ESLint 9 flat config / Prettier |
CI: every pull request and every push to
mainruns the full typecheck / lint / test suite (.github/workflows/ci.yml). Releases are cut manually viaworkflow_dispatch(.github/workflows/release.yml).
User settings — theme, UI scale, favourites, home dashboard layout, controller
shortcuts, per-plugin state — live in ~/.config/loadout/config.json
(honouring $XDG_CONFIG_HOME) and survive reinstalls and CEF profile wipes.
- Runtime: Bun — TypeScript execution +
bun build --compilesingle-binary distribution. - Overlay host: Electrobun — CEF with Bun as the main process.
- UI: React 18 + Tailwind v4 + daisyUI.
- Spatial navigation:
@noriginmedia/norigin-spatial-navigation. - Service: a system unit
loadout.service(backend, runs as root) + a user unitloadout-overlay.service(overlay).
| Document | Description |
|---|---|
| Architecture | Loader architecture, plugin structure, startup sequence |
| Plugin Development | Plugin structure, backend/frontend APIs, examples |
| Steam UI Injection | Injectable surfaces, SteamClient API, Gamescope notes |
| Overlay / Gamescope | X11 atoms, input grab, display detection |
| Gamepad Navigation | Spatial navigation, focus management |
| OS Compatibility | SteamOS, Bazzite, CachyOS specifics |
Loadout stands on the shoulders of giants. It would not be anywhere near as good as it is without the people and projects below — please go star their work.
A huge thank-you to the Decky Loader community. Years of hard work on the Linux handheld homebrew ecosystem — and the incredible plugins built on top of it — paved the way for everything here and continue to inspire it. 💛
The platform is built on:
- Bun — the TypeScript runtime + single-binary compiler.
- Electrobun — CEF with Bun as the main process.
- React, Tailwind CSS, daisyUI — the UI stack.
- norigin-spatial-navigation — gamepad-friendly focus.
And many plugins are thin, grateful wrappers around brilliant upstream projects:
- SteamGridDB — community game artwork.
- ProtonDB — Proton compatibility ratings.
- HowLongToBeat — game-length data.
- legendary — the open-source Epic Games launcher behind Store Bridge.
- lsfg-vk & decky-lsfg-vk — Lossless Scaling frame generation as a Vulkan layer.
- RyzenAdj — TDP control.
- InputPlumber — input routing.
- OpenRGB — RGB LED control.
- DeckThemes & CSS Loader — community themes and UI sound packs.
- The N64/PC recompilation community (N64: Recompiled, OpenGOAL, and friends) behind RecompHub.
Built something Loadout relies on and not listed here? Open a PR — we want to credit you.
BSD 3-Clause — see LICENSE, NOTICE, and THIRD_PARTY_LICENSES.md.











