Not just typing faster. Knowing where it breaks down. Why I am building Ritme: a calm browser trainer for accuracy, deliberate practice and meaningfully comparable progress. What I am building Ritme is my standalone browser typing trainer: words in six languages, numbers and numpad, code, custom text, keyboard guidance and local progress. Its focus is accuracy and useful feedback, not WPM alone. The app separates measurement from practice. Standard v1 provides a fixed comparison profile; adaptive exercises give more attention to weak patterns. The core runs on HTML, CSS and JavaScript, without an account or AI provider. A typing test often ends with a large number. So many words per minute. Check whether it beats the previous attempt, try again and move on. But that number does not tell me why typing felt awkward. Was I constantly correcting? Did capitals break my rhythm? Or was I simply starting too fast? That is why I am building Ritme. A calm typing trainer in which speed is an outcome, not the only goal. I want to understand what happens while I type, practise deliberately and then measure again under comparable conditions. The browser app combines words, numbers, code and custom text with keyboard guidance, adaptive practice and local progress. The standalone trainer requires neither an account nor a backend. The extra capabilities should appear when useful, rather than all demanding attention at once.12 Accuracy first. Then speed. > I do not just want to know how fast I type. I want to understand where it breaks down and where practice could help. That idea shapes the interface. A text field, a clear cursor and feedback I can leave alone until I need it. The timer starts with my first input, not while I am still looking for a setting. Keyboard hints and finger colours can support practice and disappear again for measurement.1 I am not trying to create a universal curriculum or a certified examination. This is software for a specific need: practising a skill I use every day with more intention. The value is not a bigger dashboard. It is a more useful next exercise. The same speed. A different story. Consider two illustrative tests lasting exactly one minute. Both finish with three hundred correct characters. Ritme uses five characters per standard word, so both produce sixty WPM. Both can also have one hundred per cent final-text accuracy.1 Yet the sessions can have felt very different. In the first, every character is right first time. In the second, I make twenty incorrect character attempts and later remove them. The same clean final text, but more repair work. Keystroke accuracy makes that difference visible. https://www.dewind.me/articles/en/ritme/#lab-accuracy-title In this illustrative calculation, I hold duration and final text constant. You change only the number of mistakes corrected afterwards. Correction time is already part of the minute; deleted characters do not count as text output. Backspace and Shift are not additional character attempts. That shows why I measure the result separately from the input that produced it.1 To me, that is a more useful starting point than a record alone. I can start more steadily, practise an awkward combination first or temporarily enable guidance. That is a practice decision, not a diagnosis from a single test. Measuring and practising are different jobs. A test becomes a useful comparison only when its conditions are sufficiently similar. Dutch words with punctuation are a different task from English words without capitals. A short sprint differs from two minutes of sustained typing. A different keyboard profile or corpus changes the task too. That is why Ritme has Standard · v1: sixty seconds, a fixed word pool for each language, lowercase text and no extra numbers, punctuation or finger guidance. Language, layout, corpus fingerprint and protocol version remain part of the comparison profile. Word order still changes; a matching profile is neither an identical test nor an official skill standard.13 Adaptive practice asks a different question: which patterns deserve more repetition? When an exercise adapts, its difficulty changes. A WPM record is not the right proof of progress in that setting. Ritme therefore separates that practice route from the fixed measurement.1 My practical route is simple: measure under fixed conditions, practise what needs attention, then return to the same measurement profile. Not every practice session has to be an attempt at a personal best. A keyboard is more than individual words. Sometimes I simply want to type words. Sometimes the task is numbers, capitals or a symbol I often use in code. Ritme treats those as recognisable activities, rather than hidden settings that quietly change a single overall score. | Situation | Route in Ritme | What it practises | | --- | --- | --- | | More controlled word input | Words or fixed key-row exercises, with optional guidance | Accurate input and familiar movements | | Frequent numeric input | Numbers → number row or numpad | Digit sequences with an appropriate guidance profile | | Brackets, capitals and indentation | Code → Python, HTML, CSS or JavaScript | Characters, line breaks and spaces; code is never executed | | A particular passage | Custom text | The supplied passage, not a generic benchmark | Word tests support six languages. The interface offers Dutch, English and German. Keyboard layout is a third, separate choice: US QWERTY, German QWERTZ or legacy French AZERTY. Changing the interface language should not turn an ongoing test into a different assignment.13 Finger colours are guidance, not measurement. Ritme can indicate a suggested key or the opposite hand for Shift, but it cannot see which finger someone actually uses. A numpad profile does not establish that every digit came from the physical numeric keypad either.1 Code mode retains line breaks and indentation. Tab inserts four spaces; there is no auto-completion. The task is concrete without implying that copying a snippet assesses programming knowledge. Ordinary custom text, by contrast, normalises repeated whitespace. These are deliberately different input tasks.1 More attention to what is relatively difficult. The key with the most errors is not automatically the weakest key. A frequent letter can accumulate more errors than a rare one while having a lower error rate. Bars showing error counts alone are not enough to drive adaptive practice. Ritme examines errors relative to attempts, for letters and letter pairs. Recent comparable tests and the current exercise supply the signals. A pattern first needs sufficient evidence; otherwise practice starts without a preference. Selection targets up to three weak patterns while retaining general words in the mix.1 I use a local heuristic for that selection: explicit rules over errors and attempts. Choosing from an existing word list does not require a language model. The exercise returns difficult patterns more often while general words retain variety. It is an explainable practice choice, not a promise of a fixed learning rate.1 That boundary matters to me. Adaptive is not a marketing term for a hidden intelligent system here. Its inputs, weighting and selection should be understandable. As a pattern improves, its extra weight should decrease. When evidence is missing, the app should not pretend to know my weakness. Progress without making every attempt a record. The progress view groups comparable tests. Shortened or interrupted attempts remain identifiable in history, but are not treated as ordinary record attempts. The clock keeps running while the tab is inactive. A pause outside the active window therefore does not quietly disappear from the result.1 For practice without a time limit, unlimited mode provides live feedback and a temporary summary after Stop, without saving a score or a hidden learning profile for the next session. Not everything the app shows needs to become a permanent record.1 The standalone trainer keeps settings and up to one hundred completed tests in this browser. A JSON backup retains more detail than CSV, including settings, chart points and key statistics. CSV is primarily a summary export. That distinction avoids suggesting every import can reconstruct the full measurement history.12 Local also means it is not automatically available on all my devices. A different browser, domain or relocated local file can use another storage area. Export therefore belongs in the design. I want to move my history deliberately rather than assume browser storage will last forever.1 For technical readers: from input to practice selection. A. One session, different events I keep the core in plain HTML, CSS and JavaScript. index.html defines the interface, styles.css handles presentation, and data.js contains word lists and translations. Session logic, calculations and storage live in app.js. That lets me change a dataset or explanation without adding another backend.23 At the top of app.js, APP_RELEASE checks the expected version against RITME_DATA.release. I want to prevent a partial upload from combining new logic with old data. A mismatch asks for a reload, not for progress to be erased.3 Initial language selection starts with preferredInterfaceLanguage(); a saved preference takes precedence. I keep the interface language and practice language separate. That lets someone read Dutch instructions while practising English words, without changing a test in progress.3 Within a session, I keep input attempts, retained text, skipped characters and elapsed time separate. A correction changes the final text without erasing the earlier error from keystroke accuracy. Stopping, completing and interrupting also have distinct meanings for the result profile. That separation makes progress comparisons useful.1 B. Keep units and meaning intact WPM = correct retained characters / 5 / elapsed minutes CPM = correct retained characters / elapsed minutes KPH = CPM * 60 final accuracy = correct final / (retained + skipped) * 100 keystroke accuracy = correct input attempts / all input attempts * 100 Ritme does not count literal words for WPM or physical key presses for KPH. The latter means correct characters per hour. In the chart, short input bursts differ from rolling speed. Consistency also has an explicit in-app formula: 100 / (1 + standard deviation / mean), applied to complete one-second windows of input attempts. No score is shown with fewer than five complete seconds.1 C. Fixed datasets, different selection rules Dataset draws are handled by secureInt(max) in app.js. It uses crypto.getRandomValues() with rejection sampling to select an index. Public build entropy is not a secret; each draw uses fresh cryptographic input. Fixed key-row drills deliberately use repeatable patterns because they practise a movement rather than provide a fresh word test.3 For adaptive practice, I weight up to twenty eligible earlier tests. Each older test receives fifteen per cent less weight; the current exercise counts twice. The smoothed error estimate is (errors + 0.5) / (attempts + 10). Minimum attempt counts and a lower threshold keep a single mistake from immediately steering the whole selection. Ritme’s explanation panel also sets out these rules, so understanding the choice does not require reading the source.1 D. Storage without another service The readLocal() and writeLocal() helpers are in app.js, alongside separate keys for settings, history and records. I retain scores, chart points and aggregated key statistics, not the full typed or pasted text. When browser storage is unavailable, the interface needs to say so. A temporary session must not look like saved progress.13 The standalone trainer at /ritme/ does not need a service that receives typing results. Online, the browser still requests the app files from the host. The linked Education environment is a separate application with its own setup. That boundary matters more than the word “local” on its own.1 The building blocks and where they come from. TypeSpeedTest.com provided the conceptual and visual inspiration for the calm starting point. Ritme is an independent implementation with broader practice and progress features, not an official extension or partnership with that website. That origin should remain visible.24 The language data has an origin too. Basic lists come from Monkeytype; expanded lists additionally use bundled Faker data. Ritme records sources and licences instead of presenting those words as a nationally representative corpus or an age-based curriculum. The application is offered under GPL-3.0.14 The same principle applies to score interpretation. An indicative pace category can help put a number in context. It is not a certificate, personal percentile or proof that someone performs equally well across every kind of text. An honest comparison matters more to me than an impressive-sounding result. Open it. Practise. Look back. Open Ritme ↗ View the source ↗ Start with one language and a consistent measurement profile. Look beyond speed to accuracy before corrections. Then choose a targeted exercise. Make a JSON backup when you want to preserve or move your history. A simple routine is more useful than trying a completely different test every time. The source can run locally without installing packages. A small local web server provides more predictable browser-storage behaviour than a repeatedly relocated HTML file. Practising words and calculating scores do not require an AI key.2 git clone https://github.com/fdwind1991/ritme.git cd ritme python3 -m http.server 8000 --bind 127.0.0.1 Open http://127.0.0.1:8000 A small project can solve exactly the right problem. My second brain is about rebuilding less context. Ritme addresses something smaller: typing with more control. Yet the design choice feels familiar. Not building the most features, but making a recurring activity fit the way I want to work. > A good score feels nice. Knowing what to practise next is more useful. That is the value of personal software to me. It does not need to become a large platform to have a good reason to exist. Sometimes a calm text field with honest feedback is exactly enough. Sources and notes [1] Ritme · live trainer and in-app measurement rules https://www.dewind.me/ritme/ Read on 5 October 2026; release 3.2.4. Covers the standalone trainer, not the separate Education route. Public interface and documentation, not an effectiveness study. [2] Ritme · README https://github.com/fdwind1991/ritme/blob/main/README.md Public project documentation: motivation, features, local execution and backups. [3] Ritme · app.js https://github.com/fdwind1991/ritme/blob/main/app.js Start with APP_RELEASE and the RITME_DATA check; secureInt(max) handles draws, preferredInterfaceLanguage() the initial interface language, and readLocal()/writeLocal() storage. The in-app explanation sets out the measurement rules. [4] Ritme · attribution and licences https://github.com/fdwind1991/ritme/blob/main/THIRD_PARTY_NOTICES.md Additional attribution alongside the in-app credits: TypeSpeedTest.com as inspiration; language datasets have their own source notices. [5] Publication basis and calculation example https://www.dewind.me/articles/assets/ritme/evidence/editorial-basis.md Scope of this article and its reproducible illustrative session. No personal typing results published.