Code is not slower prose. It is a different keyboard.
Source code has roughly five times the symbol density of English, almost no natural word rhythm, and capitalisation that changes meaning. Typing Coach measures code speed, symbol accuracy, bracket accuracy and capitalisation separately, because improving one does not improve the others.
JavaScript
3 snippets. Drills ( ) => . { }
PractiseTypeScript
2 snippets. Drills < > : ; | ?
PractisePython
3 snippets. Drills [ ] : _ ( )
PractiseSQL
2 snippets. Drills ( ) , . = _
PractiseHTML
1 snippet. Drills < > = " /
PractiseCSS
1 snippet. Drills { } : ; - (
PractiseJSON
1 snippet. Drills { } " : , [
PractiseMarkdown
1 snippet. Drills # - ` [ ] (
PractiseSymbols first, if you are honest about it
Most developers who feel slow at typing code are not slow at the letters - they are slow at brackets, operators and the shifted number row, which are handled almost entirely by the little fingers. The Symbol Academy trains exactly those.
All snippets
const active = users
.filter((user) => user.status === "active")
.map((user) => ({ id: user.id, name: user.name }))
.sort((a, b) => a.name.localeCompare(b.name));async function loadProfile(id) {
const res = await fetch(`/api/profile/${id}`);
if (!res.ok) {
throw new Error(`Request failed: ${res.status}`);
}
return res.json();
}const byId = records.reduce((acc, record) => {
acc[record.id] = record;
return acc;
}, {});interface Result<T> {
data: T | null;
error?: string;
meta: { page: number; total: number };
}
function unwrap<T>(result: Result<T>): T {
if (result.data === null) throw new Error(result.error ?? "empty");
return result.data;
}const users = await db.user.findMany({
where: {
active: true,
createdAt: { gte: startOfMonth },
},
select: { id: true, email: true, sessions: { take: 5 } },
orderBy: { createdAt: "desc" },
});scores = [row["wpm"] for row in sessions if row["accuracy"] > 95] top_ten = sorted(scores, reverse=True)[:10] average = sum(top_ten) / len(top_ten)
@dataclass
class Session:
wpm: float
accuracy: float
duration_ms: int
def net_words(self) -> float:
return self.wpm * (self.duration_ms / 60_000)with open("results.csv", newline="") as handle:
reader = csv.DictReader(handle)
for row in reader:
if int(row["errors"]) == 0:
clean.append(row)SELECT u.id, u.email, COUNT(s.id) AS sessions, AVG(s.net_wpm) AS avg_wpm
FROM users u
JOIN typing_sessions s ON s.user_id = u.id
WHERE s.created_at >= DATE('now', '-30 days')
GROUP BY u.id, u.email
HAVING COUNT(s.id) > 5
ORDER BY avg_wpm DESC
LIMIT 20;INSERT INTO key_performance (profile_id, key_char, attempts, correct) VALUES (?, ?, ?, ?) ON CONFLICT(profile_id, key_char) DO UPDATE SET attempts = key_performance.attempts + excluded.attempts, correct = key_performance.correct + excluded.correct;
<div class="field"> <label for="email">Email address</label> <input id="email" name="email" type="email" required /> <p class="hint" id="email-hint">We never share your address.</p> </div>
.dashboard {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 1.5rem;
padding: clamp(1rem, 4vw, 3rem);
}{
"name": "typing-coach",
"version": "1.0.0",
"engines": { "node": ">=20" },
"keywords": ["typing", "training", "touch-typing"],
"private": true
}## Getting started 1. Install dependencies with `npm install`. 2. Copy `.env.example` to `.env.local`. 3. Run `npm run dev` and open [localhost:3210](http://localhost:3210). > Note: the database is created automatically on first run.