The tech stack

Flutter and Dart for the app, Kotlin for the vibration motor, and a few carefully chosen packages.

Languages

Programming languages and what they are used for
LanguageWhat it does in HaptiBraille
DartAlmost everything: every screen, the Braille translator, the timing engine, training, analytics, storage and the tests.
KotlinThe Android code that talks directly to the vibration motor (HapticChannel.kt).
SwiftThe iPhone app shell, generated by Flutter. The iPhone vibration code is still to come.
Gradle Kotlin DSLAndroid build settings: app ID, Java version and SDK versions.
XMLThe Android manifest: app name and permissions for vibration and the microphone.
YAMLThe package list, font list and versions (pubspec.yaml).

Framework and tools

Frameworks and tools
ToolVersionWhy
Flutter3.47 stableGoogle's toolkit for building one app for Android and iPhone from a single codebase.
Dart SDK3.13The language Flutter apps are written in.
Android SDKAPI 36Builds and runs the Android app. Minimum supported phone: Android 7 (API 24).
Android NDK28.2Native build tools required by Flutter's Android build.
Gradle9.3Builds the installable APK file.
Java (JDK)17Runs Gradle and the Kotlin compiler.

Packages

Dart packages used
PackageWhat it isWhat we use it for
flutter_riverpodState managementShares the timing settings, playback, training session and progress between screens, and lets tests swap in fakes.
hive, hive_flutterOn-device databaseSaves timing settings and every training answer as plain JSON.
speech_to_textPhone speech recognitionTalk to me, dictation, and spoken training answers.
fl_chartChartsAccuracy and reading-speed lines, and the per-letter accuracy bars.
clockA replaceable clockTimes playback and answers in a way tests can control.
flutter_test, flutter_lintsTesting and code style28 automated tests, and zero analyzer warnings.

We deliberately didn't use a ready-made vibration package. Those usually offer "vibrate for X milliseconds". We need exact rhythms with different strengths played as one uninterrupted pattern, so we wrote a small bridge straight to Android's own API.

How the code is organised

The app follows Clean Architecture: separate layers with one job each. The core rules (what Braille is, how timing works, how answers are scored) don't depend on screens, storage or hardware, so they are easy to test and easy to swap.

ScreensWelcome, Home, Read, Talk to me, Train, Progress, Settings, and the animated Braille cell.
ControllersRiverpod providers that run playback and training sessions and hold your settings.
Domain (the brain)Pure Dart: Braille translator, timing engine, curriculum, analytics, data models.
DataHive storage, speech recognition, the vibration bridge, the AI agent placeholder.
AndroidKotlin HapticChannel plays the waveform on the motor.

The heart of it, in code

This Dart function turns one Braille cell into a rhythm. It is the whole idea of temporal Braille in about twenty lines (lib/domain/engine/haptic_translation_engine.dart):

if (p.cellAnchorEnabled) {                 // soft start-of-letter tick
  out.pulse(anchor(p.anchorDuration, p.anchorIntensity));
  out.silence(intra);
}
for (var slot = 0; slot < 6; slot++) {           // the six dots, in order
  if (slot > 0) out.silence(intra);              // gap between dots
  if (cell.isRaisedAt(slot)) {
    out.pulse(dot(p.dotDuration, p.intensity, slot)); // raised: tap
  } else {
    out.silence(p.emptySlotDuration);                // flat: silence
  }
}

And this Kotlin code plays the result on the phone (android/…/HapticChannel.kt). On Android 13 and newer it is marked as accessibility vibration, so it still works when the user has turned off touch vibration:

val effect = VibrationEffect.createWaveform(timings, amplitudes, -1)  // -1: play once
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
    v.vibrate(effect, VibrationAttributes.createForUsage(USAGE_ACCESSIBILITY))
} else {
    v.vibrate(effect)
}

Ready for AI

This website

Plain HTML, CSS and JavaScript with no framework and no build step. The background dot field is drawn on a canvas. The "Feel it" demo uses the same timing rules as the app and the browser's Vibration API, which works in Chrome on Android. Fonts: Bricolage Grotesque for headings and Atkinson Hyperlegible for text.

All the source code for the app and this site is on GitHub.