<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: vivek gupta</title>
    <description>The latest articles on DEV Community by vivek gupta (@vivek_gupta_09ca2a9643bf2).</description>
    <link>https://dev.to/vivek_gupta_09ca2a9643bf2</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3719938%2Fc0248ff5-5f53-454d-9c7a-a077a5c4aa4c.jpg</url>
      <title>DEV Community: vivek gupta</title>
      <link>https://dev.to/vivek_gupta_09ca2a9643bf2</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vivek_gupta_09ca2a9643bf2"/>
    <language>en</language>
    <item>
      <title>I built NinjaType: open-source typing practice with per-key feedback</title>
      <dc:creator>vivek gupta</dc:creator>
      <pubDate>Wed, 28 Oct 2026 07:20:31 +0000</pubDate>
      <link>https://dev.to/vivek_gupta_09ca2a9643bf2/i-built-ninjatype-open-source-typing-practice-with-per-key-feedback-3ck3</link>
      <guid>https://dev.to/vivek_gupta_09ca2a9643bf2/i-built-ninjatype-open-source-typing-practice-with-per-key-feedback-3ck3</guid>
      <description>&lt;p&gt;A typing test can tell you your words per minute and overall accuracy. But those two numbers leave a useful question unanswered: &lt;strong&gt;which keys should you practise next?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'm Vivek Gupta, the creator of &lt;a href="https://www.ninjatype.com/" rel="noopener noreferrer"&gt;NinjaType&lt;/a&gt;, a free, open-source typing practice app. It combines typing tests with focused finger lessons, per-key feedback, and ways to make practice more engaging.&lt;/p&gt;

&lt;p&gt;For fellow developers, here's a look at the learning experience and the implementation choices behind it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with two keys, then expand
&lt;/h2&gt;

&lt;p&gt;A large catalogue of drills can make it hard to choose where to start. NinjaType keeps the finger curriculum compact: four finger pairs, with three cumulative levels each.&lt;/p&gt;

&lt;p&gt;The index-finger path starts with just &lt;strong&gt;F and J&lt;/strong&gt;. Level two adds G, H, R, U, T, and Y; level three adds V, B, N, and M. The earlier keys stay in the later drills so progression still reinforces the basics.&lt;/p&gt;

&lt;p&gt;The other paths follow the same pattern:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Finger pair&lt;/th&gt;
&lt;th&gt;Start with&lt;/th&gt;
&lt;th&gt;Level two adds&lt;/th&gt;
&lt;th&gt;Level three adds&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Middle&lt;/td&gt;
&lt;td&gt;D, K&lt;/td&gt;
&lt;td&gt;E, I&lt;/td&gt;
&lt;td&gt;C, comma&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ring&lt;/td&gt;
&lt;td&gt;S, L&lt;/td&gt;
&lt;td&gt;W, O&lt;/td&gt;
&lt;td&gt;X, period&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pinky&lt;/td&gt;
&lt;td&gt;A, semicolon&lt;/td&gt;
&lt;td&gt;Q, P&lt;/td&gt;
&lt;td&gt;Z, slash&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;To unlock the next level, a completed session needs &lt;strong&gt;10 stars and at least 95% accuracy&lt;/strong&gt;. Stars reflect the session's speed and accuracy rating; they aren't a currency accumulated by repeating easy attempts. Each new level starts with zero stars and its own records.&lt;/p&gt;

&lt;p&gt;Previously unlocked levels remain available to retake. A replay can improve your results without moving your saved learning progress backwards.&lt;/p&gt;

&lt;p&gt;You can explore the paths in the &lt;a href="https://www.ninjatype.com/learn/" rel="noopener noreferrer"&gt;touch typing lessons&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make the keyboard useful feedback
&lt;/h2&gt;

&lt;p&gt;The learning keyboard has two views: &lt;strong&gt;Key latency&lt;/strong&gt; and &lt;strong&gt;Accuracy&lt;/strong&gt;. Practised keys get subtle border and fill colors, ranging from soft red to green. Hovering or focusing a key reveals the actual readings and sample counts.&lt;/p&gt;

&lt;p&gt;The colors use the app's existing theme variables, including &lt;code&gt;--error-color&lt;/code&gt;, &lt;code&gt;--success-color&lt;/code&gt;, and &lt;code&gt;--bg-color&lt;/code&gt;. That keeps the feedback consistent with the selected theme and avoids introducing a separate palette just for analytics.&lt;/p&gt;

&lt;p&gt;Unmeasured keys stay neutral. Missing data shouldn't look like perfect accuracy or instant typing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measuring a key is trickier than counting keystrokes
&lt;/h2&gt;

&lt;p&gt;Each key's learning metrics track four values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;LearnKeyMetric&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;attempts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;latencySamples&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;totalLatencyMs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The display reads them with explicit checks for missing samples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;readLearnKeyMetric&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;LearnKeyMetric&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;accuracy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;attempts&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attempts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;latency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;latencySamples&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;totalLatencyMs&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;latencySamples&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mistakes are attributed to the &lt;strong&gt;intended key&lt;/strong&gt;, and correcting the text doesn't erase those mistakes from the per-key feedback. Otherwise, backspacing could hide the very keys a learner needs to work on.&lt;/p&gt;

&lt;p&gt;Latency is the average interval before a correct character press. It isn't a pure measurement of a finger's reaction time: the preceding key and typing rhythm also affect it. The first press has no interval to measure, pauses longer than five seconds are excluded, and zero-time batched inserts don't create timing samples.&lt;/p&gt;

&lt;p&gt;Completed attempts contribute to saved feedback for that finger and level. The current drill can be shown alongside those records without counting a completed attempt twice.&lt;/p&gt;

&lt;h2&gt;
  
  
  The stack and state boundaries
&lt;/h2&gt;

&lt;p&gt;NinjaType uses &lt;strong&gt;Astro, React, TypeScript, and Nanostores&lt;/strong&gt;, with Apache ECharts for charts. The site runs on Cloudflare Pages; multiplayer uses a separate Cloudflare Worker with Durable Objects.&lt;/p&gt;

&lt;p&gt;Learning state stays separate from the main typing-test state. A lesson's active level is fixed for the duration of its drill, even when finishing it unlocks the next one. This lets the learner inspect their result before starting a new drill with a fresh target.&lt;/p&gt;

&lt;p&gt;Learning progress and key feedback are saved in the browser. Normal typing practice and lessons don't require an account.&lt;/p&gt;

&lt;p&gt;Beyond lessons, there are timed typing tests, &lt;a href="https://www.ninjatype.com/multiplayer-typing-race/" rel="noopener noreferrer"&gt;multiplayer typing races&lt;/a&gt;, and a Ghost Race mode based on a saved personal benchmark.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it or inspect the code
&lt;/h2&gt;

&lt;p&gt;If you're learning touch typing, start with one finger pair and use the feedback to choose what to repeat. If you're building a learning tool, the distinction between current input, completed-attempt metrics, and saved progression may be useful in your own app.&lt;/p&gt;

&lt;p&gt;NinjaType is available under the GPLv3 license. The &lt;a href="https://github.com/VivekGupta137/ninjatype" rel="noopener noreferrer"&gt;source code is on GitHub&lt;/a&gt;, including the curriculum, learning stores, and per-key measurement logic.&lt;/p&gt;

&lt;p&gt;What feedback would help you choose your next typing drill: individual keys, common letter pairs, or something else?&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: I'm NinjaType's creator. This article was drafted with AI assistance using the project's documentation and source code.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>astro</category>
      <category>react</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
