# Android

> How to run a Moonspell engine game as a normal Expo development build on an Android emulator or phone, why the react-native-webgpu plugin and minSdk 26 are required, the package name, release builds, and what differs from the Moonspell app.

> **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, the `react-native-webgpu` plugin resolves) and `npx expo prebuild --no-install --clean` in 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](https://docs.moonspell.app/start/open-source.md)).

## Prerequisites

- Android Studio with the Android SDK, and an emulator or a phone with USB debugging on.
- Java (a JDK). `expo run:android` needs Android Studio and Java installed and configured.
- Node and npm, and a real `npm install` in the game's folder.

## Run it

```bash
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`:

```bash
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`:

```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](https://docs.moonspell.app/engine/net.md)). 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 release` makes a release build, unsigned. Signing it for the Play Store is covered by [Expo: local app production](https://docs.expo.dev/guides/local-app-production/).
- In the cloud: EAS Build (`eas build --platform android`), which handles the keystore ([Expo: EAS Build](https://docs.expo.dev/build/introduction/)). Expo's development-build profiles expect `expo-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](https://docs.moonspell.app/platforms/differences.md).

## See also

- [iOS](https://docs.moonspell.app/platforms/ios.md): the same for iOS.
- [Web](https://docs.moonspell.app/platforms/web.md): the fastest way to work on a game.
- [Platform layer](https://docs.moonspell.app/engine/platform-layer.md): how the engine runs with no Moonspell app around it.
- [Quickstart](https://docs.moonspell.app/start/quickstart.md)
