A free, offline text-to-speech reader for Android and iOS, built with Kotlin Multiplatform and Compose Multiplatform.
Open-source alternative to Speechify — no subscriptions, no internet required, no data leaves your device.
---
- Import — PDF, TXT, URL, or paste text manually
- Offline TTS — native system voices on both platforms; high-quality neural Piper voices via sherpa-onnx (Android, iOS)
- Paragraph highlighting — current paragraph is highlighted in sync with playback
- Reading progress — app remembers where you stopped in every document
- Library — manage documents, mark favorites, sort by date
- Adjustable playback — speed control (0.5x–2.0x), voice gender, language, font size
TextLector uses a three-tier TTS system:
Phase 1 — Native TTS (default)
Android TextToSpeech and iOS AVSpeechSynthesizer — available immediately, no downloads.
Phase 2 — Neural TTS via Piper / sherpa-onnx sherpa-onnx bundles a pre-compiled ONNX Runtime and eSpeak-NG. Piper VITS models (~63 MB each) are downloaded on demand from HuggingFace and stored locally.
Supported voices:
| Voice | Language | Gender |
|---|---|---|
| Ruslan | Russian | Male |
| Irina | Russian | Female |
| Ryan | English | Male |
| Lessac | English | Female |
Phase 3 — Neural TTS via Supertonic supertonic-kmp — a KMP wrapper around Supertonic v3 by Supertone Inc. Flow-matching neural synthesis, 31 languages, 10 bundled voice presets (M1–M5, F1–F5). One-time model download (~265 MB), then fully offline.
Compared to Piper: higher naturalness, broader language coverage, but slower generation on mobile CPU. Tune inferenceSteps (2–12) to trade quality for speed.
All three engines share a single SwitchableTtsEngine interface. Switching happens at runtime — no app restart required. ONNX-based engines (Piper and Supertonic) use a shared TtsQueue that prefetches the next paragraph in background while the current one is playing.
Known limitation: sherpa-onnx and supertonic-kmp both bundle libonnxruntime.so. This is resolved via jniLibs.pickFirsts in build.gradle.kts combined with the static-link sherpa-onnx AAR (sherpa-onnx-static-link-onnxruntime-1.12.34.aar) which embeds ORT statically, eliminating the symbol conflict.
| Layer | Technology |
|---|---|
| UI | Compose Multiplatform |
| Architecture | MVI + ViewModel (commonMain) |
| DI | Koin Multiplatform |
| Navigation | Navigation Compose CMP (Nav2.8, typesafe routes) |
| Database | SQLDelight 2.x |
| Preferences | multiplatform-settings |
| File I/O | okio |
| HTTP | Ktor Client 3.x |
| HTML parsing | Ksoup (Jsoup KMP port) |
| Neural TTS | sherpa-onnx (Piper VITS) + supertonic-kmp (Supertonic v3) |
| PDF (Android) | PdfBox-Android |
| PDF (iOS) | PDFKit + Vision OCR fallback |
composeApp/
├── commonMain/ # shared UI, ViewModels, domain, data
│ ├── domain/ # models, repository interfaces, use cases
│ ├── data/ # SQLDelight, repository implementations
│ ├── ui/ # Compose screens and components
│ └── platform/ # expect declarations (TTS, FileReader, etc.)
├── androidMain/ # Android actuals + sherpa-onnx engine
├── iosMain/ # iOS actuals
└── jvmMain/ # Desktop (planned)
iosApp/
├── TTS/ # IosSherpaEngine, SherpaOnnxTtsBridge
├── PDF/ # PdfTextExtractor, IosPdfPageExtractor
└── Utils/ # IosFileDownloader, IosTarExtractor
- Android Studio Meerkat or later
- Xcode 15+
- JDK 17+
- Kotlin 2.0+
./gradlew :composeApp:assembleDebugOpen iosApp/iosApp.xcodeproj in Xcode and run on a simulator or device.
The sherpa-onnx XCFramework is included via CocoaPods / local framework — no additional setup required.
./gradlew :composeApp:compileKotlinAndroid
./gradlew :composeApp:iosArm64MainKlibrary- PDF, TXT, EPUB, FB2, manual text import
- URL import
- Native TTS (Android + iOS)
- Neural TTS via Piper / sherpa-onnx (Android + iOS)
- Camera / OCR import
- Neural TTS via Supertonic — 31 languages, flow-matching
- JVM / Desktop support
- Background playback (MediaSession / AVAudioSession)


