youcoded commited on
Commit
c12f9e0
·
verified ·
1 Parent(s): 6fa2895

Simpler, clearer model card: what it is, how it was built, how to use it, how well it does

Browse files
Files changed (1) hide show
  1. README.md +70 -147
README.md CHANGED
@@ -15,12 +15,14 @@ tags:
15
  - ctc
16
  - gru
17
  - swipe-keyboard
 
 
18
  model-index:
19
- - name: swipe-typing (T1)
20
  results:
21
  - task:
22
  type: other
23
- name: Swipe decoding, English, first guess right (all swipes)
24
  dataset:
25
  name: bench-2000 (2,000 real English swipes, FUTO MIT test split)
26
  type: custom
@@ -30,7 +32,7 @@ model-index:
30
  name: first guess right, all swipes
31
  - task:
32
  type: other
33
- name: Swipe decoding, English, right word in top 3 (all swipes)
34
  dataset:
35
  name: bench-2000 (2,000 real English swipes, FUTO MIT test split)
36
  type: custom
@@ -40,179 +42,100 @@ model-index:
40
  name: right word in top 3, all swipes
41
  ---
42
 
43
- # youcoded/swipe-typing ("T1"): swipe-typing decoder for English, Spanish, French and German
44
 
45
- > **Headline:** on 2,000 real English swipes (bench-2000, all swipes) the first guess is right 90.40 % of the time against 93.32 % for FUTO's engine, 2.9 points behind; no Gboard swipe comparison was made (Gboard swipe typing was not measured).
46
 
47
- A small neural network that turns the path of a finger across an on-screen keyboard into a ranked list of words, for the Swipe Keyboard: an open-source swipe-typing and word-prediction add-on for KDE Plasma's on-screen keyboard. One model serves all four languages and any layout, because the keys are given to it as positions.
48
 
49
- Models trained and released by Destin Moss via youcoded.ai, 2026.
50
-
51
- Code, scripts and tests: https://github.com/itsdestin/swipe-keyboard/tree/feature/179-engine-in-keyboard (development branch; it moves to the main branch when merged). How every number below was measured: [methods page](METHODS.md) (published copy: METHODS.md).
52
-
53
- **Status: an opt-in alternative engine.** In the keyboard it is used only when the user creates an `own-engine` file; FUTO's engine remains the default. On English it is **not yet as accurate** as FUTO's engine (see the numbers below).
54
-
55
- ## What it is, and what it is not
56
-
57
- - A 1-D convolution stack followed by a 2-layer bidirectional GRU, trained with the CTC loss (Graves et al., 2006) on the sequence of keys the finger passes over. Read from the shipped file's header: 2 convolution layers of width 96 (kernel 5), a 2-layer bidirectional GRU with hidden size 128, a pointer head of width 32, 64 time steps of 8 features, float32 weights. That is about **529,000 parameters** and a **2.1 MB** file (2,115,260 bytes).
58
- - Layout independent: the network does not have a fixed output per key. For every time step it scores each key from the finger's offset to that key (in key widths) plus the key's size, with one small shared scoring network ("pointer head"). A QWERTY, AZERTY, QWERTZ or Spanish keyboard with an n-tilde key therefore use the same weights.
59
- - It outputs probabilities over keys, not words. Words come from a **lexicon beam search** over a word list (a trie of key sequences), so every answer is a word in the list, scored as network evidence + word frequency + the n-gram sentence prior when previous words are known + a **geometric cross-check** (a separate, classical shape-matching decoder, blended in with weight 40).
60
- - **Fallback:** with no model file, a damaged one, or an empty result, the geometric decoder alone answers; swiping never depends on the network.
61
- - It is **not** a handwriting or general gesture recogniser, and it cannot output a word that is not in the word list (words missing from the list are always misses; see limitations).
62
-
63
- ## Intended use
64
-
65
- On-device swipe typing. The keyboard supplies: the finger path (x, y, time), the live rectangles of the keys, a verified word list for the language, and, when available, the previous words of the sentence. Settings used for all reported results: frequency weight 0.06, geometric cross-check weight 40, beam width 48.
66
-
67
- Speed on one CPU thread: about 3.5 ms per swipe (measured on a loaded desktop, plain `-O3` build).
68
-
69
- Not intended for: security-sensitive input, identifying people, or any use that stores or sends a user's swipes. The keyboard does not record swipes unless a user switches on a separate opt-in recorder.
70
-
71
- ## File format
72
-
73
- File `swipe-model.bin`, read by our own plain C++ runner (`engine/src/swipe_net.cpp`, no machine-learning library). Magic `SKNN`, version 1, little-endian.
74
-
75
- | Part | Content |
76
  |---|---|
77
- | Magic | the 4 bytes `SKNN` |
78
- | Header (u32 each) | version (1); data type (0 = float32, 1 = float16); time steps (64); input features per step (8); key features (4); number of convolution layers; kernel size; GRU hidden size; GRU layers; pointer-head width |
79
- | Convolution widths | one u32 per convolution layer |
80
- | Tensors | in order: for each convolution layer weight [out][in][kernel] and bias; for each GRU layer and direction (forward, then reverse) input weights, recurrent weights and both biases (PyTorch gate order r, z, n); pointer head: A (weight, bias), B (weight, bias), w (weight, bias); blank head (weight, bias) |
81
-
82
- Because the layer sizes are stored in the file, a bigger or smaller network needs no code change. The loader validates every header field against fixed ranges and refuses a file that is too short for the tensors it declares. **There is no checksum inside this file**, unlike the next-word models; integrity comes from the SHA-256 the keyboard pins (`941272a7017229a73e07d1c0bd766757e739174db3a42fb622c26256b8c46110`), checked before the engine loads it. A mismatch means geometry-only mode.
83
-
84
- Input per step (64 steps resampled by arc length): x, y, dx, dy, log elapsed time, fraction of the path done, step length, log path length.
85
 
86
- ## Training data and licences
87
 
88
- | Source | What was taken | Licence |
89
- |---|---|---|
90
- | FUTO's public swipe collections (swipe.futo.org: `swipe-1` training split, `swipe-2` to `swipe-5`) | real swipes, English QWERTY; `swipe-5` also holds real German, French and Spanish swipes on their own layouts | MIT (Copyright (c) 2025 FUTO; text kept in `FUTO-SWIPE-DATA-LICENSE.txt`) |
91
- | Our own synthetic swipes | 100,000 each for Spanish, French and German, generated by `scripts/swipe/synth_swipes.py` on approximate layouts; no synthetic English in T1 | ours; MIT |
92
- | Word lists (used at run time, not part of the model file) | the benchmarks used 260,000-word lists counted from FineWeb / FineWeb-2 and Tatoeba; the keyboard plugin loads the language profile's verified `.combined` list instead (top 100,000 words for es/fr/de) | ODC-By 1.0 and CC BY 2.0 FR, see the language-pack notices |
93
 
94
- What exactly went into T1: about 317,000 swipes from a session-level 35 % subset of `swipe-1` to `swipe-4`, `dev.jsonl` (54,000 English swipes from FUTO's MIT recordings; about 47,600 left for training after 10 % of its sessions were held out for validation), all `swipe-5` groups (French, German and Spanish slices repeated three times, each on its own layout), plus the synthetic swipes. 30 epochs, batch 512.
 
 
 
 
95
 
96
- **Never trained on:**
97
- - `bench-2000`, `test-extra-10000` and `test.jsonl` (FUTO's MIT test split) and the validation sessions: an overlap check dropped any swipe whose id or session appeared in them; no overlapping swipe or session was found in any collection (56,005 swipe keys and 768 sessions checked).
98
- - The HeliBoard gesture test sets (CC BY-SA): used for **testing only**. No HeliBoard data is in the training set, which is why the model's licence is not share-alike.
99
- - No FUTO model weights were used or fine-tuned; the architecture and code are our own, written from published literature (CTC, lexicon-constrained gesture decoding, SHARK2, dynamic time warping).
100
 
101
- ## Training procedure and hardware
102
 
103
- - Script: `scripts/swipe/train.py` (PyTorch, CTC loss on the collapsed key sequence; AdamW, one-cycle learning rate). Recipe "R2": dropout 0.2, board-geometry augmentation and speed augmentation, random whole-trace shifts of about 0.2 key.
104
- - Hardware: one desktop NVIDIA RTX 4070 Super. **Training time for T1: 16 minutes** for 30 epochs.
105
- - Model selection: search settings (frequency weight, cross-check weight, beam) were fixed on the clean validation split before scoring. `bench-2000` and `test-extra-10000` were scored once for the earlier model F1 and a second time for T1, the final model only; T1 was chosen using other data (trv8k, `swipe-5` slices, clean3000), see below.
106
- - Not recorded in the repository: the exact command line of the T1 run and the random seed (the script default is 1).
107
 
108
- ## Evaluation
109
 
110
- All figures are percentages. "First guess" = the top suggestion is the target word; "top 3" = it is among the first three. **Denominators matter:** "all swipes" counts every swipe, including those whose target word is not in the word list (those can never be right); "in-dictionary" counts only swipes whose target is in the word list.
111
 
112
- ### English, real swipes (FUTO's MIT test split), scored once for the final model
 
 
113
 
114
- | Test set | Swipes | All swipes: first / top 3 | In-dictionary: first / top 3 |
115
- |---|---|---|---|
116
- | bench-2000 | 2,000 (1,958 in-dictionary) | 90.40 / 94.95 | 92.34 / 96.99 |
117
- | test-extra-10000 | 10,000 (9,781 in-dictionary) | 90.77 / 94.97 | 92.80 / 97.10 |
118
- | clean3000 (3,000 swipes, diagnostic) | 3,000 | not stated in the source | 93.13 / 97.37 |
119
-
120
- ### Against FUTO's engine (English)
121
-
122
- | Set | FUTO engine, first / top 3 | This model, first / top 3 | Denominator |
123
- |---|---|---|---|
124
- | bench-2000 | 93.32 / 96.56 | 90.40 / 94.95 | all 2,000 swipes (FUTO figure: all 2,000 through our recogniser harness) |
125
- | bench-2000 | 93.32 / 96.56 | 92.34 / 96.99 | ours: in-dictionary swipes only; the FUTO figure is not restricted this way, so this row is not like-for-like |
126
- | clean3000 | 92.5 / 96.3 (2,909 swipes FUTO's tool can score) | 93.13 / 97.37 | ours: in-dictionary only (97.5 % of the 3,000) |
127
 
128
- Plainly: on all swipes of bench-2000 the model is **2.9 points behind FUTO on first guess and 1.6 behind on top 3** (90.40 vs 93.32; 94.95 vs 96.56). On the swipes whose word is in the dictionary the gap on bench-2000 is about 1 point on first guess and 0 on top 3 (92.34 vs 93.32; 96.99 vs 96.56), but FUTO's number does not exclude out-of-dictionary swipes, so do not read that row as a win. The stated target (match FUTO's 93.3 / 96.6 on all swipes of bench-2000) was **not reached**.
 
 
 
129
 
130
- ### Other languages: real swipes from HeliBoard (test only)
131
 
132
- HeliBoard gesture test sets: 5,000 real swipes per language, case-insensitive, accents kept, per-swipe real board geometry, 260,000-word lists. First guess / top 3, all 5,000 swipes. "FUTO engine today" used the older, smaller word packs, so the two columns do **not** use the same dictionary and are not like-for-like.
 
 
 
 
 
133
 
134
- | Language | This model (T1) | FUTO engine today (old packs) |
135
- |---|---|---|
136
- | English | 82.1 / 91.5 | 84.5 / 93.4 |
137
- | Spanish | 81.1 / 89.8 | 77.7 / 85.1 |
138
- | French | 71.7 / 85.9 | 64.6 / 76.9 |
139
- | German | 73.3 / 82.3 | 69.7 / 76.4 |
140
 
141
- Dictionary coverage of the HeliBoard targets with the 260,000-word lists: Spanish 97.5 %, French 98.2 %, German 91.9 % (English: not stated for this list). With the older 100,000-word lists the coverage was Spanish 90.5 %, French 83.0 %, German 80.0 %, which caps accuracy.
142
 
143
- Two common HeliBoard English boards with near-identical geometry differ by 5 to 7 points for every decoder tested, so the spread between rows is partly the swipers, not the layout handling.
144
 
145
- ### Real multilingual swipes from FUTO's `swipe-5`, on their own layouts (validation slices)
146
 
147
- | Model | French | German | German (QWERTZ) |
148
  |---|---|---|---|
149
- | F1 (previous model, English + synthetic only) | 79.8 | 84.7 | 81.6 |
150
- | T1 (this model) | 83.2 | 90.2 | 83.2 |
151
-
152
- First guess. The source calls these "val slices" of the collection; it does not describe how they were separated from training in that table, so treat them as a development comparison, not a final test.
153
-
154
- ### Synthetic Spanish, French and German (not human)
155
-
156
- The synthetic noisy sets (`bench/languages/synthetic-noisy-v1-*.json`, 400 words each) were scored with the **older FUTO-engine keyboard**, not with this model: Spanish 89.00 / 98.25, French 83.25 / 93.00, German 87.75 / 97.00 (first / top 3). We have **no synthetic-set score for T1 in the sources**. Synthetic English calibration: real 92.8 / 96.4 against synthetic 92.3 / 96.0. These sets only cover dictionary words and assume people swipe other languages like English.
157
-
158
- ### Sentence-context prior (experiment)
159
-
160
- When the keyboard knows the previous words, the English n-gram prior lifts T1 on a 7,198-swipe set (trv8k) from 92.19 / 95.79 to 93.10 / 95.92 (first / top 3, all swipes; in-dictionary 95.28 / 98.17). That is +0.9 first guess. The set is a held-back 5 % slice of FUTO's `train.jsonl` that still has sentence text. Single-word swipes (bench-2000) carry no sentence, so the headline numbers above do not include this gain.
161
-
162
- ### Gboard
163
-
164
- Gboard's swipe typing was **not measured** (any version, learning on or off).
165
-
166
- ## Known limitations
167
 
168
- - **English is about 1 to 3 points behind FUTO's engine** on single in-dictionary words and about 3 on all swipes; the target was not met. More real English data barely helped: six times more real English swipes moved the clean English numbers by +0.4 on one set and 0 on another, so we stopped (agreed stopping rule).
169
- - **French and German are lower than English** on HeliBoard (71.7 and 73.3 first guess vs 82.1). The sources do not isolate the cause. One measured factor is dictionary coverage of the targets (German 91.9 % even with the 260,000-word list; compounds are the likely reason, not verified). The spread between swipers on the same layout is also large (5 to 7 points).
170
- - **Accent twins** (Spanish que / qué, como / cómo; French ou / où) share one swipe path. The shape cannot decide between them; the sentence prior has to, and without previous words the choice is frequency only.
171
- - **Short words (3 letters or fewer)** were the other common miss when the older FUTO-engine keyboard was scored on synthetic Spanish, French and German swipes; we have not repeated that breakdown for T1.
172
- - **Out-of-dictionary words are always misses.** About 2.5 % of English targets are not in the word list (bench-2000: 42 of 2,000).
173
- - **Spanish, French and German training is partly synthetic** (100,000 each) plus the real `swipe-5` slices. Only English has large real recordings. Real volunteers' swipes per language are still missing, so non-English numbers are less certain than English ones.
174
- - **Layout handling is approximate where we had no real key rectangles.** The AZERTY/QWERTZ/Spanish layouts used for synthetic training are approximations (`make_layouts.py`); the real keyboard supplies live rectangles at run time. Key pitch, not drawn key width, is the unit, because boards with gaps between keys were 20 % off otherwise.
175
- - **An earlier validation error was found and corrected** (see the incident below). Every number on this page comes from the corrected, clean sets.
176
- - No bias or fairness evaluation was done. The training swipers are volunteers on FUTO's collection; their handedness, device and language mix are not documented here.
177
 
178
- ### The inflated-validation incident (2026-10-22)
179
 
180
- For a day, the validation sets named `devhold-5000`, `tune-2000` and `tune-10000` were believed to be held out. They were not: all of their swipes were contained in `dev.jsonl`, the training set. Every "devhold" figure was therefore measured on training swipes, including 91.5 % for the first CPU model and the 93.02 / 96.07 that looked like a tie with FUTO for the first GPU model ("gpu1"). **Those numbers are retracted.** The swipe helper found it; the coordinator verified it (100 % of ids and sessions overlapped); the fix was to hold out whole recording sessions from `dev.jsonl` as validation (`scripts/swipe/split_by_session.py`) and to select only on that, keeping `bench-2000` and `test-extra-10000` as final-only sets. The honest first read on fresh swipes put the model 2 to 4 points behind FUTO, not level.
181
-
182
- ## How to reproduce
183
-
184
- From a checkout of https://github.com/itsdestin/swipe-keyboard/tree/feature/179-engine-in-keyboard. You need FUTO's public swipe data (MIT, from swipe.futo.org) and a CUDA GPU; scoring needs the Qt build.
185
-
186
- ```
187
- # 1. Collect the evaluation keys so training data can be checked against them
188
- python -I scripts/swipe/holdout_ids.py holdout-final.json bench-2000.jsonl test-extra-10000.jsonl test.jsonl
189
- python -I scripts/swipe/holdout_ids.py holdout-val.json <validation sessions>.jsonl
190
- # 2. Filter FUTO's collections (drops two-finger and invalid swipes and anything overlapping the evaluation files)
191
- python -I scripts/swipe/assemble_futo.py DATA_DIR OUT_DIR holdout-final.json holdout-val.json
192
- # 3. Synthetic swipes for es / fr / de (own generator, different from the test generator)
193
- python -I scripts/swipe/synth_swipes.py --words words.tsv --layout scripts/swipe/layouts/azerty.tsv --n 100000 --out synth-fr.jsonl
194
- # 4. Convert and train (see the script header for all options; recipe R2 flags: --dropout 0.2 --geo-aug ... --time-aug ...)
195
- python -I scripts/swipe/prep_data.py ...
196
- python -I scripts/swipe/train.py --train real.npz --train synth-fr.npz:scripts/swipe/layouts/azerty.tsv --val val.npz \
197
- --out run --epochs 30 --batch 512 --conv 96,96 --hidden 128 --layers 2 --dropout 0.2 --device cuda
198
- # 5. Score (first guess / top 3, ms per swipe)
199
- build/tools/swipekb-swipenet-bench --model run/model.bin --words words.tsv --layout scripts/swipe/layouts/qwerty.tsv bench-2000.jsonl
200
- ```
201
-
202
- The exact flag values of the shipped T1 run were not recorded; the sizes above follow the run table in `docs/active/specs/2026-10-23-own-swipe-engine.md`, and the geometry and speed augmentation strengths should be taken from the script's help text. Expect to need a few attempts to match the published numbers. More commands, and the Gboard and FUTO measurements, are on the [methods page](METHODS.md).
203
-
204
- ## Licence of the model file
205
 
206
- `swipe-model.bin`: **MIT**. Training data: real swipes from FUTO's swipe.futo.org collections (MIT, Copyright (c) 2025 FUTO) plus our own synthetic swipes; the file contains no FUTO model weights.
207
 
208
- Required credits when redistributing:
209
 
210
- - **Models trained and released by Destin Moss via youcoded.ai, 2026**
211
- - The MIT licence text of FUTO's swipe data (`FUTO-SWIPE-DATA-LICENSE.txt`, shipped with the model).
212
- - The release notice `MODELS-NOTICE.md` (release `models-2026-10-23`).
213
 
214
- The word lists the model is used with carry their own attribution terms (FineWeb / FineWeb-2: ODC-By 1.0; Tatoeba: CC BY 2.0 France; for English also the Android LatinIME word list, Apache-2.0). They are separate files and not part of the model.
215
 
216
- ## Credit
217
 
218
- Models trained and released by Destin Moss via youcoded.ai, 2026.
 
15
  - ctc
16
  - gru
17
  - swipe-keyboard
18
+ datasets:
19
+ - futo-org/swipe.futo.org
20
  model-index:
21
+ - name: swipe-typing
22
  results:
23
  - task:
24
  type: other
25
+ name: Swipe decoding, English, first guess right (all swipes, model alone)
26
  dataset:
27
  name: bench-2000 (2,000 real English swipes, FUTO MIT test split)
28
  type: custom
 
32
  name: first guess right, all swipes
33
  - task:
34
  type: other
35
+ name: Swipe decoding, English, right word in top 3 (all swipes, model alone)
36
  dataset:
37
  name: bench-2000 (2,000 real English swipes, FUTO MIT test split)
38
  type: custom
 
42
  name: right word in top 3, all swipes
43
  ---
44
 
45
+ # swipe-typing
46
 
47
+ A small offline neural network that reads the path of a finger across an on-screen keyboard and says which keys it passed over. With a word list and a search step it turns a swipe into ranked word guesses. One model serves English, Spanish, French and German on any key layout. It is built for the [Swipe Keyboard](https://github.com/itsdestin/swipe-keyboard), an open-source swipe-typing and word-prediction add-on for KDE Plasma's on-screen keyboard, which uses it as its only swipe engine from version 0.5.0 (earlier versions used FUTO's engine).
48
 
49
+ Models trained and released by Destin Moss via youcoded.ai, 2026
50
 
51
+ | | |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  |---|---|
53
+ | File | `swipe-model.bin`, 2,115,260 bytes (2.1 MB) |
54
+ | Languages | English, Spanish, French, German (any layout) |
55
+ | Shape | 2 convolution layers (96 wide, kernel 5), 2-layer bidirectional GRU (128 hidden units), small shared "pointer" scorer; about 529,000 numbers, stored as float32 |
56
+ | Licence | MIT |
57
+ | SHA-256 | `941272a7017229a73e07d1c0bd766757e739174db3a42fb622c26256b8c46110` |
 
 
 
58
 
59
+ It outputs probabilities over keys, not words, so it is not a word decoder on its own and cannot be used for handwriting or other gestures.
60
 
61
+ ## How it was built
 
 
 
 
62
 
63
+ - **Data:** real swipes from FUTO's public [swipe.futo.org](https://huggingface.co/datasets/futo-org/swipe.futo.org) collections (MIT licence, Copyright (c) 2025 FUTO): about 317,000 swipes from 35% of the recording sessions of collections 1 to 4, FUTO's `dev` set (about 47,600 English swipes after holding out validation sessions), and all of collection 5, which includes real French, German and Spanish swipes on their own layouts (repeated three times). Plus 100,000 synthetic swipes each for Spanish, French and German from our own generator (`scripts/swipe/synth_swipes.py`). No synthetic English.
64
+ - **Held out:** FUTO's test split (including the 2,000-swipe `bench-2000` and 10,000-swipe `test-extra-10000` slices) and the validation sessions were never trained on. A check dropped any swipe whose id or session appeared in them, and found no overlap in any collection (56,005 swipe keys and 768 sessions compared). The HeliBoard gesture sets (CC BY-SA) were used for testing only.
65
+ - **No FUTO code or model weights** were used; the network and code are our own, written from published methods (CTC, lexicon-constrained gesture decoding, SHARK2).
66
+ - **Training:** `scripts/swipe/train.py` (PyTorch): CTC loss on the sequence of keys the finger passes, AdamW, one-cycle learning rate, 30 epochs, batch 512, dropout 0.2, and random changes to key geometry, speed and position of the trace during training. About 16 minutes on one NVIDIA RTX 4070 Super. The exact command line and augmentation strengths of the shipped run were not recorded (the script's random seed default is 1).
67
+ - **How it handles any layout:** the network has no fixed output per key. At each of 64 time steps it scores every key from the finger's offset to that key (in key widths) and the key's size, with one small shared scorer, so QWERTY, AZERTY, QWERTZ and Spanish keyboards use the same weights.
68
 
69
+ ## How to use it
 
 
 
70
 
71
+ **Who it is for:** developers of an on-screen keyboard who want offline swipe recognition (about 3.5 ms per swipe on one CPU thread, no machine-learning library) in these four languages.
72
 
73
+ **Pieces you need besides this file:** a word list for the language (every answer is a word from it), and the search step. The project's code does all of this, in plain C++20 (needs only Qt 6 Core 6.8+ for Unicode): the network is `engine/src/swipe_net.cpp`, the word search `engine/src/swipe_beam.cpp`. The search is a beam search over a trie of the word list; its scores add the network's key probabilities, word frequency, a sentence prior when previous words are known, and a classical shape-matching cross-check. With no model file, or a damaged one, the engine falls back to the shape-matching decoder alone.
 
 
 
74
 
75
+ **Input:** the path (x, y, time) is resampled by arc length to 64 steps with 8 features each (x, y, dx, dy, log elapsed time, fraction of path done, step length, log path length), plus the live key rectangles. `scripts/swipe/swipelib.py` (`trace_to_input`) shows the same preparation in Python.
76
 
77
+ **Minimal example** (C++, from the project's `engine/README.md`; word lists are in GitHub release `language-files-2026-10-23-apostrophes`):
78
 
79
+ ```cpp
80
+ #include "swipekb/language_engine.hpp"
81
+ using namespace swipekb;
82
 
83
+ LanguageEngine engine;
84
+ LanguageEngineSetup setup;
85
+ setup.language = "en"; // "en", "es", "fr" or "de"
86
+ setup.dictionary = "en_swipe.combined"; // required word list
87
+ setup.ngram = "en.ngram"; // optional sentence prior
88
+ setup.swipeModel = "swipe-model.bin"; // this file
89
+ if (!engine.init(setup)) { /* word list unreadable */ }
 
 
 
 
 
 
90
 
91
+ // trace: finger samples {x, y, t_ms}; keys: one {letter, x, y, w, h} per letter key, same pixel space
92
+ auto guesses = engine.recognize(trace, keys, contextWords("I think we"), 3);
93
+ // guesses[0].word is the best guess; engine.hasModel() is false if the model was not loaded
94
+ ```
95
 
96
+ **Limits**
97
 
98
+ - English trails FUTO's engine by 1 to 2 points on first guess (table below). Spanish and German also trail on the HeliBoard swipes; French is ahead.
99
+ - A word missing from the word list is always a miss (42 of 2,000 English test targets, 2.1%). German is hit hardest: the shipped German list contains only 80% of the HeliBoard targets.
100
+ - Only English has large real recordings. Spanish, French and German training is partly synthetic (plus a small amount of real data), so their results are less certain.
101
+ - Accent twins (Spanish que / qué, French ou / où) have the same swipe path; the sentence prior or word frequency has to choose.
102
+ - The training swipers are FUTO volunteers; their handedness, devices and language mix are not documented. No bias evaluation was done.
103
+ - The file has no internal checksum; the keyboard checks its SHA-256 before loading it.
104
 
105
+ **File layout** (little-endian, `engine/src/swipe_net.cpp`): the 4 bytes `SKNN`; ten 32-bit integers (version 1, data type 0 = float32 or 1 = float16, time steps 64, features per step 8, key features 4, number of convolution layers 2, kernel size 5, GRU hidden size 128, GRU layers 2, pointer width 32); one integer per convolution layer (its width); then the weights as plain arrays: each convolution layer (weights [out][in][kernel], bias), each GRU layer and direction (input weights, recurrent weights, two biases, PyTorch gate order r, z, n), the pointer scorer and the "no key" (blank) scorer. Layer sizes come from the header, so a different size needs no code change.
 
 
 
 
 
106
 
107
+ ## How well it does
108
 
109
+ All figures are percent. **First guess** = the top suggestion is the intended word; **top 3** = it is among the first three. Real swipes are from volunteers, never trained on: `bench-2000` = 2,000 English swipes spread evenly over FUTO's MIT test split. "FUTO's scorer counts" = the 1,946 of them that FUTO's tool can score (target of 2+ letters, more than 2 trace points).
110
 
111
+ **English, bench-2000**
112
 
113
+ | What was measured | Swipes counted | First guess | Top 3 |
114
  |---|---|---|---|
115
+ | This model alone (frequency prior only) | all 2,000 | 90.40 | 94.95 |
116
+ | Whole keyboard (this model + word list + sentence prior, previous words given), build 0.5.0-0.5 | all 2,000 | 90.10 | 93.10 |
117
+ | Whole keyboard, same build | 1,946 FUTO's scorer counts | 91.47 | 94.55 |
118
+ | FUTO's engine in the previous keyboard (0.3.x), previous words given | 1,946 FUTO's scorer counts | 93.32 | 96.56 |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
 
120
+ So on the same 1,946 swipes the keyboard trails FUTO's engine by 1.9 points on first guess and 2.0 on top 3. FUTO's own command-line tool with its stock dictionary scored 92.50 / 95.43 on these swipes, which makes the gap 1.0 and 0.9; the 93.32 / 96.56 figure comes from running FUTO's models in the project's test harness with the project's word list. (The keyboard also hides stretched and rare spellings from guesses 2 and 3, so they are real words more often; that costs a little top-3 accuracy against the model alone.)
 
 
 
 
 
 
 
 
121
 
122
+ **Other languages, first guess, real swipes from HeliBoard** (test only; no sentence given; lower-case, accents kept, apostrophes ignored; "Swipes" is how many of the 5,000 per language were scored)
123
 
124
+ | Language | Swipes | Whole keyboard (this model) | FUTO's engine in keyboard 0.3.1 |
125
+ |---|---|---|---|
126
+ | English | 4,576 | 80.5 | 84.5 |
127
+ | Spanish | 4,790 | 75.9 | 77.7 |
128
+ | French | 4,999 | 68.2 | 64.6 |
129
+ | German | 4,980 | 64.7 | 69.7 |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
 
131
+ Each engine used the word list it shipped with, and FUTO's was older and smaller, so this is a comparison of shipped keyboards, not of models alone. Two common English boards with nearly the same geometry differ by 5 to 7 points for every decoder tested, so some of the spread between rows is the swipers, not the decoder.
132
 
133
+ No comparison with Gboard's swipe typing was made. Test-set details and how each number was produced: [METHODS.md](METHODS.md).
134
 
135
+ ## Licence and credit
 
 
136
 
137
+ `swipe-model.bin` is **MIT**. The real training swipes come from FUTO's swipe.futo.org (MIT, Copyright (c) 2025 FUTO); keep the licence text `FUTO-SWIPE-DATA-LICENSE.txt` from this repository when you redistribute. Also keep:
138
 
139
+ - the credit line above (Models trained and released by Destin Moss via youcoded.ai, 2026) and `NOTICE.md`.
140
 
141
+ The word lists used with the model carry their own attribution terms (FineWeb / FineWeb-2: ODC-By 1.0; Tatoeba: CC BY 2.0 FR; English also Apache-2.0); they are separate files.