ruffle-android, a native Android shell around the Ruffle runtime
An Android application for ruffle.rs
At a glance
- What is it?
- ruffle-android is the Android application that hosts the Ruffle runtime, built as a Rust library crate behind an Android Activity, with wgpu for rendering and cargo-ndk and Gradle handling the native build. The project describes itself as being in a very early stage, and its TODO list is the most accurate description of the project available.
- Who is it for?
- ruffle-android suits a developer who wants to run Ruffle content on an Android device today, or who wants to help finish an Android port of a runtime that otherwise has no first-class mobile presence. It does not suit a user who expects a finished application, because the project says it is in a very early stage and the checklist shows the soft keyboard, controller input, error handling and app store distribution are all still open.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 5 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
An application shell around a library, not a game
ruffle-android is a native Android application for Ruffle, and the very first thing the README says about it is that it is in a very early stage. That sentence is the most useful line in the document, because it sets the expectation for everything else.
The structure is what makes the project interesting. The Cargo manifest describes a package named `ruffle_android` whose crate type is `cdylib`, which means it is a native shared library, not a binary. The Android side is a separate Gradle project with an `app/` directory, and the Rust side is a library that the application loads.
The dependency list makes the division of labour clear. The Ruffle crates themselves are pulled as git dependencies from the main repository on the master branch, so `ruffle_core`, `ruffle_common`, `ruffle_render_wgpu`, `ruffle_video_software` and `ruffle_frontend_utils` all track upstream master rather than a pinned release. On the Android side, `android-activity` is used with the `game-activity` feature, which is the modern way to put a native rendering surface inside an Android Activity, and `jni` provides the Java interface, and `android_logger` handles logging.
The rendering backend is `wgpu`, and the comment above it in the manifest says it has to follow Ruffle with this, which is a candid way of saying that the Android port is version-coupled to the renderer upstream. The audio path uses `ndk` with the `audio` feature, and the `rustls` dependency is there for the TLS in `ruffle_frontend_utils`, with `rustls-platform-verifier` because certificate verification needs initialising on Android.
The audience for the code is a developer. The audience for the application is a user who wants to run content on a phone. The project is one of those pieces of infrastructure that exists because the desktop and web versions of a runtime do not cover a platform.
Four APK variants and what they are for
The README spends more space on the download options than on anything else, which reflects the practical reality of shipping a native app to Android.
The latest release should have a few `.apk` files uploaded as assets, and the documentation walks through which one you want.
For the vast majority of modern phones, tablets, single board computers and small game consoles, you need the `arm64-v8a` version. The `armeabi-v7a` version is for older 32-bit ARM hardware. The `x86_64` version covers rare Intel or Microsoft tablets, Chromebooks, and running on a PC with Android-x86, or in Waydroid or something similar. The `x86` version is there mostly for completeness. And the `universal` version should work on all four of the above architectures, but it is huge.
That list is a useful reality check about the state of Android hardware in 2026. The arm64 case is the normal case, and the x86_64 case is the one that catches people out, because it is the right answer for a Chromebook running Android or an emulator on a laptop, and the wrong answer for a phone.
The app is also on F-Droid under the package `rs.ruffle`, which is the distribution path for people who do not want to install APKs from a release page.
The trade-off on the universal build is explicit and honest. One APK that runs everywhere is convenient, and it is large. Four targeted APKs are a smaller download each and you have to pick the right one. The README does not give the sizes, so the practical advice is to pick the architecture variant and fall back to universal only if you are unsure.
For a first real use, the sequence is: install the app, open a file, and see whether the content runs. The project is on F-Droid, so that is the low-friction route for a user, and it also means you get updates through the F-Droid client rather than hunting for release assets.
What the TODO list reveals about the state of the port
The README carries a checklist, and reading it as a status document is more informative than any feature list would be.
Still open: the ability to show the built-in virtual keyboard for text input, controller and gamepad input, a custom keyboard overlay, error and panic handling, a loading animation or spinner, an alternative audio backend using OpenSL ES for Android below 8, a proper storage backend, resolving design glitches in styling and theming including immersive mode and window insets for holes and notches, publishing to various app stores, bundling demo animations and games, the ability to load content from well known online collections, and history, favourites and similar features.
Two of those deserve a note. The soft keyboard item is the one that most affects whether the app is usable for a given piece of content, because Flash content of that era was frequently text entry. And the OpenSL ES audio backend for Android below 8 is a compatibility item that tells you the default audio path assumes a reasonably modern device.
The DONE list is more revealing, because it shows the shape of the work. It includes cleaning up most things, cross-platform build instructions, a context menu UI backend, logging, a navigator backend for fetch and open browser, touch and mouse input, keyboard input with a physical keyboard connected or through `scrcpy`, splitting into a separate repository, the ability to open an SWF by entered or pasted URL, unglitchified rendering with proper scaling, centring and letterboxing, CI that builds APKs, a simplified build process after switching to `cargo-ndk` with the corresponding Gradle plugin, filtering files down to `.swf`, audio volume fixes after switching to GameActivity, signing the APK, support for 32-bit ARM phones, support for x86 and x86_64 tablets, and not building an intermediate APK just for the shared libraries, so only native libs are built.
The keyboard input entry carries a caveat that is worth reading twice. It says only with a physical keyboard connected or through `scrcpy`, and it links to a winit pull request, which is where the fix came from.
Two items on the DONE list are marked as not applicable rather than done: unbreaking the regular build on CI and cleaning up the commit history, both noted as no longer relevant after the repository split. That is a project that has been reorganised and is still settling.
The build chain, and why the native side is the hard part
Building from source is documented in CONTRIBUTING.md rather than in the README, and the manifest plus the Makefile-adjacent files give the shape of it.
The repository is a hybrid. There is a `Cargo.toml` and `Cargo.lock` for the Rust library, a `build.gradle.kts`, a `settings.gradle.kts`, a `gradle.properties` and both `gradlew` and `gradlew.bat` for the Android application, and a `rust-toolchain.toml` pinning the Rust side. The `vendor/` directory and the `.gitmodules` file suggest the native dependencies are vendored, and `fastlane/` is present for the release automation.
The interesting dependency is the one whose version has to be negotiated with the upstream project. `ruffle_core` and its siblings are git dependencies on the master branch of the ruffle repository, and the features requested are listed explicitly: audio, symphonia, mp3,nellymoser, lzma, default_compatibility_rules and default_font. Each of those is a compatibility decision, and symphonia and mp3 in particular are about audio decoding, which is a common source of missing-feature behaviour in emulator projects.
The comment above the `wgpu` dependency is the one that says the most. It reads that they have to follow Ruffle with this. In other words, the renderer version is not chosen by this project, it is dictated by what the upstream runtime does, and the Android port has to keep pace.
That is a real constraint on anyone who wants to contribute. A change in upstream Ruffle's rendering interface breaks this build, and the fix is a version bump plus whatever adaptation the interface change requires.
The `rustls-platform-verifier` and `rustls` combination is a case in point. The comments explain the reasoning: reqwest verifies certificates through `ruffle_frontend_utils`'s `rustls-tls` feature, which on Android needs to be initialised with its Kotlin component included, and the version has to stay semver-compatible with the one reqwest uses so they share a copy. The crypto provider choice is `ring` rather than `aws-lc-rs`, and the reason given is that `aws-lc-rs` needs CMake to build, which would complicate the Android build.
What this port is not
It is worth being clear about the boundaries, because the project is young and the README's own wording invites over-reading.
First, this is not a game. It is an application for running content that another project, the Ruffle runtime, interprets. The distinction matters for the licensing question, which is the sharpest line in the repository. The Cargo manifest declares the crate as licensed MIT OR Apache-2.0, while the repository's own licence field is a custom type that GitHub cannot classify, with the LICENSE.md file in the tree holding the terms. Those are two different licences for two different things, the code and the project as distributed, and the file in the repository is the one to read.
Second, this is not a finished application. The README's first line says it is in a very early stage, and the checklist backs that up. There is no error handling, which means a content file that triggers a bug in the runtime produces whatever the platform does with an unhandled condition rather than a message. There is no app store presence, so the distribution options are F-Droid and sideloading a release asset.
Third, there is no content library. The TODO list mentions bundling demo animations and games and loading content from well known online collections, and both are unchecked, with the online collection item carrying a parenthetical hesitation. Whatever content you want to run, you supply yourself.
Fourth, it does not cover iOS or the web. Those would be separate projects, and the release history here is Android only.
That leaves a narrow but real niche: an Android port of an open-source runtime, at an early stage, with the interesting engineering being the native build integration rather than the application features. For a developer who wants to run Ruffle content on a phone today it is usable, because the APKs are published and F-Droid carries it. For a user expecting a polished app store product, it is not that yet.
Editorial conclusion
ruffle-android suits a developer who wants to run Ruffle content on an Android device today, or who wants to help finish an Android port of a runtime that otherwise has no first-class mobile presence. It does not suit a user who expects a finished application, because the project says it is in a very early stage and the checklist shows the soft keyboard, controller input, error handling and app store distribution are all still open. Verify first that your device architecture has an APK variant that fits, that the Ruffle master branch the app tracks has not broken the build since your last checkout, and that the content you want to run is within the compatibility the runtime currently implements.
Frequently asked questions
What is ruffle-android?
It is a native Android application for Ruffle, the open-source runtime for Flash content. The Rust side is built as a cdylib crate named ruffle_android, and the Android application loads it, with wgpu for rendering and cargo-ndk plus Gradle producing the native libraries.
Which APK should I download for my phone?
For most modern phones, tablets, single board computers and small consoles you want the arm64-v8a version. armeabi-v7a is for older 32-bit ARM hardware, x86_64 covers rare Intel tablets, Chromebooks and Android-x86 or Waydroid, x86 is mostly for completeness, and the universal build works everywhere but is very large.
How do I build ruffle-android from source?
The README points at CONTRIBUTING.md for the details. The repository mixes a Cargo project for the Rust library with a Gradle Android application, and the build uses cargo-ndk with the corresponding Gradle plugin to produce the native libraries.
Does ruffle-android support game controllers and the on-screen keyboard?
Not yet. The README checklist lists the ability to show the built-in virtual keyboard and controller or gamepad input as still to do. Keyboard input is listed as working only with a physical keyboard connected or through scrcpy.
What licence does ruffle-android use?
The Cargo manifest declares the crate as MIT OR Apache-2.0, while the repository carries a LICENSE.md file and a licence field that GitHub cannot classify. The file in the repository holds the terms for the project as distributed.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/ruffle-rs-ruffle-android)Was this useful?