| # Staticplay SD1 Mobile (On-Device, Android) | |
| Goal: ship a **real offline** Android app (no servers, no cloud) that runs an SD1-class model **on-device** using **NNAPI** (via a native runtime). | |
| This repo currently includes a working offline SD1.5 ONNX "pack" runner (unzip + inspect + generate). | |
| ## Key points (no surprises) | |
| - Use a **prebuild** / **native build** (Expo Go won't cut it for native inference). | |
| - "Offline" means the **model files are on the phone** (bundled in the APK or copied into app storage). | |
| - ONNX Runtime is integrated via a tiny **Android native module** (no JSI auto-install). | |
| ## Run (Android) | |
| ```bash | |
| cd /home/tiny/projects/staticplay-sd1-mobile | |
| export ANDROID_HOME=/home/tiny/Android/sdk | |
| export PATH="$ANDROID_HOME/platform-tools:$PATH" | |
| npx expo prebuild --clean | |
| npx expo run:android # debug (uses Metro) | |
| ``` | |
| For a fully offline build (no Metro), install the release build: | |
| ```bash | |
| cd /home/tiny/projects/staticplay-sd1-mobile/android | |
| NODE_ENV=production ./gradlew :app:installRelease | |
| ``` | |
| Then press "Test native ONNX (NNAPI -> CPU)" in the app. | |
| ## Bundle With Expo (Windows, release APK) | |
| Use these commands from this project root. | |
| ```powershell | |
| # 1) Install dependencies (once) | |
| npm install | |
| # 2) Regenerate native Android project if config/plugins changed | |
| npx expo prebuild --platform android --clean | |
| # 3) Build release APK | |
| cd android | |
| ./gradlew.bat :app:assembleRelease | |
| # 4) Install to connected phone (USB debugging on) | |
| ./gradlew.bat :app:installRelease | |
| ``` | |
| APK output path: | |
| `android/app/build/outputs/apk/release/app-release.apk` | |
| Notes: | |
| - This v2 build uses package id `com.anonymous.staticplaysd1mobile.v2`, so v1 can stay installed. | |
| - Some setup buttons can take longer than expected (especially inspect actions). | |
| - Generation works offline but is still being optimized for speed. | |
| - Website: `https://staticplay.co.uk` | |
| ## SD1.5 pack + generation (offline) | |
| 1) Put the SD1.5 ONNX pack zip at the app external directory as: | |
| `/storage/emulated/0/Android/data/com.anonymous.staticplaysd1mobile/files/sd15-onnx-pack.zip` | |
| 2) In the app: | |
| - (Optional) `Show app external pack path` (for sanity check) | |
| - `Unzip SD15 pack from external` (one-time; can take minutes) | |
| - `Load tokenizer (bundled)` (no external files required) | |
| - Toggle backend: | |
| - `Backend: NNAPI (experimental)` = fastest, can be unstable on some devices/drivers | |
| - `Backend: CPU (safe)` = slower, but avoids NNAPI driver issues | |
| - `Generate 512x512 (quick 4)` to prove the end-to-end pipeline | |
| - `Generate 512x512 (quality 20)` for higher quality (slower) | |
| 3) Output images save to the same app external folder as `sd_out_*.png`. | |
| ## SD1 plan (high level) | |
| 1) Pick an SD1 distilled variant (LCM/Turbo-style) to keep steps ~4-8. | |
| 2) Export components to ONNX (text encoder, UNet, VAE decoder). | |
| 3) Quantize mostly UNet (INT8) + keep VAE in FP16/BF16. | |
| 4) Run inference via a native runtime with NNAPI; fall back to CPU if NNAPI can't compile a node. | |
| 5) Ship the ONNX files in-app (or as offline downloadable packs). | |
| ## V2 notes (2026-02-12) | |
| - This branch installs as a separate app id (`com.anonymous.staticplaysd1mobile.v2`) so v1 stays on device. | |
| - Guided mode is enabled to show one button at a time for easier setup flow. | |
| - Some actions can take longer than expected, especially: | |
| - `Inspect pack UNet` | |
| - `Inspect pack text_encoder` | |
| - `Inspect pack vae_decoder` | |
| - first unzip/generation after launch | |
| - Overall generation is currently functional but still slower than target. Ongoing work is focused on speed improvements. | |
| - Public site: `https://staticplay.co.uk` | |