Android
Not built on the authors' machine.
npm run android(prebuild, Gradle, an emulator or a phone) was never run for this page. What was run:npx expo config(valid, thereact-native-webgpuplugin resolves) andnpx expo prebuild --no-install --cleanin a throwaway copy of the template, which succeeded and gave the values below. It is not known from our own runs whether the app launches, whether WebGPU draws on an emulator, or how fast it runs. The steps are Expo's standard ones, checked against Expo SDK 58's docs and the template's config.
On Android your game is an ordinary Expo app: expo run:android generates the native project, builds it with Gradle, installs it and loads the game's JavaScript from Metro. The engine needs nothing from the Moonspell app to run there (What is and is not open).
Prerequisites
- Android Studio with the Android SDK, and an emulator or a phone with USB debugging on.
- Java (a JDK).
expo run:androidneeds Android Studio and Java installed and configured. - Node and npm, and a real
npm installin the game's folder.
Run it
npm install
npm run android
npm run android is expo run:android. The first run generates android/ (Expo's prebuild), compiles a debug build, installs it on an emulator or a connected phone and starts Metro. Later runs skip prebuild while android/ exists. Arguments after -- go to expo run:android:
npm run android -- --device
--device lets you pick a connected phone or an emulator.
The template does not include expo-dev-client, so this is a plain debug build that loads from Metro, without the development launcher screen.
What app.json needs
The Android part of the template's app.json:
{
"expo": {
"orientation": "portrait",
"android": { "package": "com.rofi.crescentmoon.game" },
"plugins": ["react-native-webgpu"]
}
}
| Key | Why |
|---|---|
plugins: ["react-native-webgpu"] | Required on Android. react-native-webgpu's native code needs API level 26 (androidMinSdkVersion in its package.json), and its build.gradle does not go lower for an app with a lower minSdkVersion, so such an app fails with a manifest-merger error. Expo's default is 24. The config plugin raises the app's minSdkVersion to 26 |
android.package | Change com.rofi.crescentmoon.game to an id of your own. It becomes the namespace and applicationId |
orientation: "portrait" | Becomes screenOrientation="portrait" on the activity |
What the prebuild check generated:
| Setting | Value |
|---|---|
android.minSdkVersion | 26 (Android 8.0) |
newArchEnabled | true |
hermesEnabled | true |
namespace, applicationId | com.rofi.crescentmoon.game |
screenOrientation | portrait |
The template sets no scheme, so your build opens no deep links. If you add one, useLaunchRoomId() reads ?room=CODE from the link through React Native's Linking (Net). Not tested.
Emulators and phones
On Android, react-native-webgpu runs Dawn on Vulkan. Its docs warn that Android emulators may fall back to a software adapter: slow, and some features may be missing. Test performance on a real phone.
Release builds
At a pointer level only; none of this was run for the engine:
- Locally:
npx expo run:android --variant releasemakes a release build, unsigned. Signing it for the Play Store is covered by Expo: local app production. - In the cloud: EAS Build (
eas build --platform android), which handles the keystore (Expo: EAS Build). Expo's development-build profiles expectexpo-dev-client, which the template does not include.
Different from the Moonspell app
| Inside the Moonspell app | Your Expo build | |
|---|---|---|
| How the game loads | The app loads the game's Android export into its own runtime | Your app is the game; Metro serves it in development |
| Exit button | Back to the app's games | exitToShell() does nothing; the template's App.tsx unmounts the game and shows its "Play" button |
| Paused off screen | Yes, while the app's feed keeps the game loaded but not shown | Never: the game is always active |
getLaunchUrl(), getShareUrl(), roomInviteUrl() | The app passes the links in | null |
| The app's feed and share links | Yes | No |
| Game backend | The game's own Worker | GAME_BACKEND_URL is null in the template: leaderboards, presence and rooms stay on this device until you deploy the backend and set it |
Icons on Android draw the Material Symbol twin of each SF Symbol, and Skia text uses the sans-serif family where the engine names a system font. These and the other differences are on Platform differences.
See also
- iOS: the same for iOS.
- Web: the fastest way to work on a game.
- Platform layer: how the engine runs with no Moonspell app around it.
- Quickstart