/switch/ folder..apk files in sdmc:/Viridite/apks/.Viridite from hbmenu (Atmosphere CFW required).sdmc:/Viridite/compat_log.txt for the full error log.| Input | Action |
|---|---|
| D-pad / either stick / swipe | Move through the game list |
| L / R / ZL / ZR | Page a screenful at a time |
| Stick click | Jump to the top of the list |
| A / tap a game | Launch |
| B | Manage the selected game — or back out of any screen |
| X | Reinstall and launch (re-extracts the APK) |
| Y | Rescan the APK folder |
| − | About and credits |
| + | Quit |
A USB keyboard works too — arrows and Page Up/Down to move, Enter to launch, Escape to back out. Every screen accepts the same inputs; there is no screen that only answers the D-pad.
For a game with controller support, the launcher asks before starting it — showing the controller it can actually see attached (Pro Controller, handheld, a Joy‑Con pair, or a single Joy‑Con held sideways) alongside a touch-screen option. It only asks when there's a genuine choice: docked with one controller, or a game with no controller path, goes straight to launching.
Whichever you pick is also used to patch the game's own controller-help screen, so the diagram it shows is the controller in your hands rather than the Android pad it was drawn for.
| Input | Action |
|---|---|
| R / ZR | Accelerate |
| L / ZL | Brake and reverse |
| D-pad / face buttons | Menus |
| + | Pause |
That mapping was read out of the game's own binary rather than guessed: its touch handler and its controller handler write the same two pieces of state, and the accelerator is the right-hand pedal on screen.
Viridite checks for a newer release when it starts, on a background thread, so the game list stays responsive while it does. If a newer version exists you're asked whether to install it; nothing is downloaded or replaced unless you say yes.
Installing replaces the launcher and both Translation Core binaries together — a launcher from one release paired with a Core from another isn't a combination anyone tests. The new files are written and checked before anything existing is touched, so a failed update leaves your install exactly as it was.
A console with no network connection simply skips the check and says so in the log. Nothing about the launcher depends on being online.
Press B on any game for its settings, which mirror Android's own app-info screen:
| Action | Removes | Keeps |
|---|---|---|
| Clear cache | Extracted libraries and assets | Saves and settings |
| Clear storage | Everything for that game, saves included | Nothing |
| Delete | The APK and all of its data | Nothing |
Each shows how much space it would actually reclaim, measured off the card, and each needs a second press to confirm. Clearing the cache just means the game re-extracts next time it launches — progress is untouched.
There's also a per-game framerate cap here, for titles that run hot or drain battery faster than they need to.
Viridite ships no games. You supply the APK.
Copy the .apk (or .xapk) onto your SD card at
/Viridite/apks/ and it appears in the launcher next time you open it.
That is the whole process — there is no import step, no conversion, and nothing
to register.
Split APKs are handled: an .xapk is unpacked and its
config.* splits merged automatically, so games distributed that way work
without being reassembled by hand first.
The game must contain native ARM code — arm64-v8a or
armeabi-v7a. Viridite runs the game’s own machine code directly, so
a title written purely in Java or Kotlin has nothing for it to run. The launcher tells
you which architecture an APK contains before you launch it.
Where you get the APK is your responsibility. Use a copy you have lawfully obtained. Viridite does not download games, does not link to pirated sources, and cannot grant you rights in someone else’s software — see Legal.
If a game works, or fails in an interesting way, tell us: the compatibility list is built entirely from reports.
Installing a game writes a forwarder to
/switch/Viridite Games/<package>.nro. hbmenu lists it with the
game’s own name, icon and version, and selecting it goes straight into the game
rather than into Viridite’s list.
Uninstalling removes it again, and every time the launcher scans your APKs it reconciles that folder against what is actually installed — so it can never show an entry for a game that is no longer there.
Every forwarder is the same small stub binary. What differs is the metadata appended to it and the filename, which is the Android package name; the stub reads that back from its own path and hands off to the Translation Core. Nothing is compiled on your console.
The author line credits the game’s developer first, then Viridite — Fingersoft | Viridite Contributors — and the version shown is the game’s version, not Viridite’s, because the entry describes the game it will start.
Launching a game runs a loading screen while the APK is unpacked and its libraries are mapped. When the game is genuinely ready, the gem animation you have been watching completes: it pulses, breaks, and the game’s own icon bursts through, against a short jingle. The screen falls to black and the game fades up out of it.
The jingle is a MIDI file rendered on the console rather than a recording —
a few hundred bytes of notes, synthesised as struck crystal, which is what Viridite is
named after. You can rewrite it: it lives in
tools/make_jingle.py in the launcher repository.
Viridite’s launcher is built on Material 3 (Material You). Every colour in the interface is a Material role — not a hand-picked value — generated from tonal palettes derived from a seed colour, so text always keeps a readable contrast against whatever sits behind it, whichever theme you pick.
Press ZL on the game list (or P on a keyboard) to
open the theme picker. Six schemes ship — Viridite, Mint and
Violet, each in light and dark. Each row previews itself in its own colours.
Your choice is remembered in sdmc:/Viridite/theme.txt.
Type is Roboto Flex and the icons are Material Symbols Rounded, both from Google Fonts and bundled with the launcher — nothing is downloaded at runtime.
Hold Y for five seconds on the game list to run a self-test. It checks the things that otherwise only show up as a failed launch: that both Translation Core binaries are present and are real builds rather than truncated copies, that the bundled artwork and system font load, that the SD card is genuinely writable (written, read back and deleted — a full or read-only card passes a simple existence check), free space, and every game’s manifest, install and architecture.
It will tell you, for example, that a game is marked installed but has no native
libraries behind it — a half-finished extract that looks fine in the list and then
fails at launch. Results are written to sdmc:/Viridite/selftest.txt and
also copied into launcher_log.txt.
With a game highlighted, press X on the self-test screen. Viridite hands over to the Translation Core, which loads that game exactly as a real launch does — extracting, mapping every native library, relocating, and resolving every imported symbol — then stops before any of the game’s own code runs, and hands straight back with the result.
It takes a couple of seconds and reports the load time and the number of unresolved symbols. Zero unresolved is what you want; a number climbing after an update means a function the game needs has gone missing from the compatibility layer.
Android games declare which way up they expect to be, and Viridite honours it:
A portrait game under a landscape rule is drawn at its own aspect down the middle of the screen with black bars either side, never stretched.
One hardware note: a Switch console has no motion sensor of its own — the accelerometers live in the Joy-Cons. While attached they are rigidly part of the console and report its orientation faithfully; detached, they report their own, which says nothing about how the console is being held. So on a standard Switch the one case that asks for auto-rotation is the one case that cannot sense it, and Viridite holds the last known orientation rather than spinning the screen because a Joy-Con was set down on a table. A Switch Lite has a built-in sensor and does not have this problem.
Viridite is split into two pieces, in two separate repos, that chain-load into each other rather than being one monolithic binary:
Builds Viridite.nro — the picker. Scans sdmc:/Viridite/apks/, tags each APK by native ABI, shows the list, and on launch hands off to the right engine via envSetNextLoad(path, argv), passing the package name as argv[1]. No ELF loader, JNI shim, or game-engine code of its own.
Builds Viridite-Translation-Core-x64.nro — the real engine: ELF loading, JIT, the JNI/Bionic compat layer, audio, sensors. Always expects a package name in argv[1]; not meant to run standalone. Also builds an -x32 placeholder — 32-bit games aren't supported yet (no public AArch32 libnx toolchain).
Why split it this way: a Switch process runs in one execution state (AArch64 or AArch32) for its whole lifetime, so the picker and a hypothetical future 32-bit engine can't be one process. Splitting the picker from the engine means adding a real 32-bit engine later is "point the launcher at a new NRO," not "rewrite everything." Splitting them into separate repos keeps the launcher (small, stable) decoupled from the engine (where almost all development happens) — "the app won't launch" and "the game crashes" issues land in the right place.
Requires devkitPro with devkitA64 and libnx installed.
Viridite is split across two repositories as well as two NRO binaries — clone both as siblings to build everything and get a drag-to-SD-card layout:
export DEVKITPRO=/opt/devkitpro
git clone https://github.com/Viridite/Viridite.git
git clone https://github.com/Viridite/VNX-Translation-Core.git
cd Viridite
./build_all.sh
Output: testingbuild/Viridite.nro (copy to sdmc:/switch/) and testingbuild/Viridite/ (copy the whole folder to sdmc:/switch/Viridite/).
Just want the launcher on its own? make in the Viridite repo builds Viridite.nro by itself.
switch-sdl2 switch-sdl2_image switch-sdl2_ttf
switch-libpng switch-libjpeg-turbo switch-minizip
switch-mesa switch-glad switch-curl switch-mbedtls
Prebuilt releases (no toolchain needed) are published on the Releases page — each one bundles the launcher plus both Translation Core builds, ready to drag onto an SD card. Releases are built and published automatically by a GitHub Actions workflow (manually triggered by org staff) that also generates an in-depth changelog and refreshes the in-app contributor credits from every repo in the org.
The mechanism forwarders are built on is available by hand too. Pass a game’s package name (e.g. com.fingersoft.hillclimb) as the first argument when launching Viridite.nro and it boots straight into that game, skipping the app-list picker — the same idea as a RetroArch forwarder jumping straight to a ROM+core rather than RetroArch’s own browser.
This is libnx’s standard envSetNextLoad(path, argv) chain-load, which is what every homebrew forwarder uses. If the requested package isn’t installed, Viridite falls back to the normal picker with an on-screen notice.
Since forwarders are generated automatically on install, you rarely need this — it is useful for your own scripts and shortcuts.
Tried a game and want it on the compatibility page? Use the submission form — no GitHub account needed. Attach the .apk you tested directly (uploads straight to storage — real APKs running 50-150MB are no problem) or paste a download link, say where you got it from, and attach or paste the three log files from sdmc:/Viridite/: launcher_log.txt, compat_log.txt, and <package>/log.txt. There's no length limit on the log fields either way.
It's handled entirely by automation, and there's no maintainer in the loop:
If you gave a GitHub username, it shows up on the Credits page and in the launcher's own About screen under Testers — that field isn't verified, it's just for display.
The full technical history — every root-caused bug, every hardware measurement, the complete changelog — lives in the Viridite README.