Skip to content

Instantly share code, notes, and snippets.

@shricodev
Created March 4, 2025 10:33
Basic typing test application (generated by Claude 3.7 AI Model). This gist is for one of my blog post comparison of AI Models.
"use client";
import { useEffect, useState, useRef, useCallback } from "react";
export default function Home() {
const [text, setText] = useState("");
const [userInput, setUserInput] = useState("");
const [startTime, setStartTime] = useState<number | null>(null);
const [endTime, setEndTime] = useState<number | null>(null);
const [isActive, setIsActive] = useState(false);
const [rawWPM, setRawWPM] = useState(0);
const [adjustedWPM, setAdjustedWPM] = useState(0);
const [mistakes, setMistakes] = useState(0);
const inputRef = useRef<HTMLInputElement>(null);
const sampleTexts = [
"The quick brown fox jumps over the lazy dog.",
"Coding is the art of telling a computer what to do.",
"Next.js is a React framework for building web applications.",
"TypeScript adds static type definitions to JavaScript for better tooling.",
"Practice makes perfect when it comes to improving your typing speed.",
];
const getNewText = useCallback(() => {
const randomIndex = Math.floor(Math.random() * sampleTexts.length);
setText(sampleTexts[randomIndex]);
}, []);
useEffect(() => {
getNewText();
}, [getNewText]);
const resetTest = () => {
setUserInput("");
setStartTime(null);
setEndTime(null);
setIsActive(false);
setRawWPM(0);
setAdjustedWPM(0);
setMistakes(0);
getNewText();
if (inputRef.current) {
inputRef.current.focus();
}
};
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const input = e.target.value;
// Start the timer on the first keypress
if (!startTime && input.length === 1) {
setStartTime(Date.now());
setIsActive(true);
}
// Count mistakes
let currentMistakes = 0;
for (let i = 0; i < input.length; i++) {
if (text[i] !== input[i]) {
currentMistakes++;
}
}
setMistakes(currentMistakes);
// Calculate WPM
if (startTime) {
const timeElapsed = (Date.now() - startTime) / 1000 / 60; // in minutes
const wordsTyped = input.length / 5; // Assuming 5 characters per word
const wordsCorrect = (input.length - currentMistakes) / 5;
setRawWPM(Math.round(wordsTyped / timeElapsed || 0));
setAdjustedWPM(Math.round(wordsCorrect / timeElapsed || 0));
}
setUserInput(input);
// Complete the test if user has typed the entire text
if (input.length === text.length) {
setEndTime(Date.now());
setIsActive(false);
}
};
// Render text with character comparisons
const renderText = () => {
return text.split("").map((char, index) => {
let className = "";
if (index < userInput.length) {
// Character has been typed
className =
userInput[index] === char
? "text-green-500"
: "text-red-500 bg-red-100";
} else if (index === userInput.length) {
// Current character to type
className = "bg-gray-200 text-gray-800";
}
return (
<span key={index} className={className}>
{char}
</span>
);
});
};
return (
<div className="flex flex-col text-black items-center justify-center min-h-screen bg-gray-50 p-4">
<div className="w-full max-w-2xl bg-white p-8 rounded-lg shadow-lg">
<h1 className="text-3xl font-bold text-center mb-6">Typing Test</h1>
<div className="mb-6 p-4 bg-gray-100 rounded-lg min-h-[100px] text-lg leading-relaxed">
{renderText()}
</div>
<div className="mb-6">
<input
ref={inputRef}
type="text"
value={userInput}
onChange={handleInputChange}
className="w-full text-zinc-800 p-3 border border-gray-300 rounded text-lg"
placeholder="Start typing here..."
disabled={!isActive && userInput.length > 0}
autoFocus
/>
</div>
<div className="flex flex-col sm:flex-row gap-4 justify-between mb-6">
<div className="stats flex gap-4">
<div className="p-3 bg-blue-50 rounded-lg">
<h2 className="font-semibold">Raw WPM</h2>
<p className="text-xl font-bold">{rawWPM}</p>
</div>
<div className="p-3 bg-green-50 rounded-lg">
<h2 className="font-semibold">Adjusted WPM</h2>
<p className="text-xl font-bold">{adjustedWPM}</p>
</div>
<div className="p-3 bg-red-50 rounded-lg">
<h2 className="font-semibold">Mistakes</h2>
<p className="text-xl font-bold">{mistakes}</p>
</div>
</div>
<button
onClick={resetTest}
className="bg-blue-500 text-white py-2 px-4 rounded hover:bg-blue-600"
>
{userInput.length > 0 ? "Reset Test" : "New Test"}
</button>
</div>
{endTime && startTime && (
<div className="border-t pt-4">
<h2 className="text-xl font-semibold mb-2">Test Results</h2>
<p>
Time: {((endTime - startTime) / 1000).toFixed(2)}s | Raw WPM:{" "}
{rawWPM} | Adjusted WPM: {adjustedWPM} | Accuracy:{" "}
{Math.round(((text.length - mistakes) / text.length) * 100)}%
</p>
</div>
)}
</div>
</div>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment