Live product

CleverKeys

cleverkeys.app

Shipped On-device MLAndroid
CleverKeys screenshot

A swipe decoder with no alphabet inside it. Most gesture keyboards hard-code a letter per output column. CleverKeys' encoder takes key coordinates as a tensor: [2,64] resampled touch path, [64,2] key centers, a validity mask. Every key is embedded from its own (cx, cy), never from its slot index, so the same 2.9 MB file decodes QWERTY, Dvorak, AZERTY and QWERTZ without retraining. That design wasn't elegance — it was a bug fix. The original recipe sampled a fixed cosine field at the 26 QWERTY centers, and that basis matrix has rank 23, not 26. Three emission directions, concentrated on d/f/g/h/j/k and e/r/t/y/u/i, were structurally unreachable at any width, depth or dataset size. It was found by inspecting singular values, not by training longer.

Then we taught it layouts nobody has ever swiped on. Layout-agnostic in architecture is not layout-agnostic in accuracy: the first model scored 63% on Dvorak, worse than a dumb geometric matcher. Fixing it meant warping real human swipes onto imaginary keyboards — decomposing each recorded path against the ideal polyline through the word's keys via a monotone dynamic program, then re-anchoring the residual on a different geometry with an arc-length remap that stays absolute near each key and stretches only in transit. Dvorak went 63 → 92. The same machinery, given virtual letter ids, transplants English motor noise onto Russian words on a ЙЦУКЕН layout — which is how the shipped Cyrillic model hits 85% top-1 on real swipes having trained on zero real Russian rows, because the only Cyrillic corpus in existence is eval-only by licence.

And every number above survives an audit, because the test set fights back. The held-out split is protected by a content-addressed seal: each row hashes to its normalized word plus exact float64 coordinate bytes, the 2,400 fingerprints are committed to the repo, and any evaluation whose loaded rows overlap the sealed set is refused — renamed, shuffled or sliced. Four reads are on the ledger, each one pre-registered in a document committed before the decode ran. One reported result was retracted in place when a fifth seed broke it. The app itself is 174k lines of Kotlin under GPL-3.0, ships two independent decoders so no script is ever left without swipe, is the only open-source keyboard with working gesture typing inside Termux, and requests no network permission at all — so the browser demo, which CI byte-compares against the APK's own weights, is the only version that could phone home even in principle.

  • 2.9 MB, 1.5 M params, fp16 weights — layout is a model input (layout_keys[64,2]), so one graph serves every Latin arrangement: Dvorak 91.8, Spanish 89.5, AZERTY 84.5, QWERTZ 84.0 top-1.
  • Found a rank-23 architecture defect by SVD: a fixed cosine key basis made 3 of 26 emission directions unreachable at any scale; the fix (learned per-key geometry embedding, MatMul not Einsum) also removed every node XNNPACK can't delegate.
  • Dvorak accuracy 63 → 92 via monotone-DP residual transplant — real human swipes re-anchored onto keyboard geometries nobody has ever typed on, with the near-key dwell band remapped absolutely (proportional remap drops endpoint hit rate 0.87 → 0.68).
  • A Russian model trained on zero real Russian swipes — English motor residuals transplanted onto ЙЦУКЕН polylines by geometric vertex-count matching; 85.07% top-1 on the real Yandex corpus, which is eval-only by licence.
  • The test split refuses to be re-read: 2,400 committed content hashes, refusal on any overlap (renamed, shuffled, or a 120-row prefix), and an append-only ledger of four authorised, pre-registered unsealings.
  • A 231k-node lexicon trie hand-fitted to the ART object layout — parallel arrays instead of LinkedHashMap children cut it from ~42 MB to ~19 MB, and linear scan beats hashing at a 1.44 average branching factor.
  • Build fails at median > 150 ms or p90 > 250 ms through the production decode path in CI; the browser demo's weights are cmp-checked byte-for-byte against the APK's on every release.
  • Zero INTERNET permission (VIBRATE + READ_USER_DICTIONARY only) — language packs install by Storage Access Framework, not download.