Skip to content

Instantly share code, notes, and snippets.

@shricodev
Created March 4, 2025 10:53
Show Gist options
  • Save shricodev/deceac7ba54b9cb5b597ae4e1f3220b7 to your computer and use it in GitHub Desktop.
Save shricodev/deceac7ba54b9cb5b597ae4e1f3220b7 to your computer and use it in GitHub Desktop.
Basic typing test application (generated by GPT-4.5 AI Model). This gist is for one of my blog post comparison of AI Models.
"use client";
import { useEffect, useRef, useState } from "react";
const sampleText = "The quick brown fox jumps over the lazy dog.";
export default function Home() {
const [userInput, setUserInput] = useState("");
const [startTime, setStartTime] = useState<number | null>(null);
const [timeElapsed, setTimeElapsed] = useState(0);
const [intervalId, setIntervalId] = useState<NodeJS.Timeout | null>(null);
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
inputRef.current?.focus();
}, []);
useEffect(() => {
if (startTime) {
setIntervalId(
setInterval(() => {
setTimeElapsed((Date.now() - startTime) / 1000);
}, 1000),
);
}
return () => {
if (intervalId) clearInterval(intervalId);
};
}, [startTime]);
const handleReset = () => {
setUserInput("");
setStartTime(null);
setTimeElapsed(0);
inputRef.current?.focus();
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const val = e.target.value;
if (!startTime) setStartTime(Date.now());
setUserInput(val);
if (val === sampleText) {
if (intervalId) clearInterval(intervalId);
}
};
const getCharClass = (char: string, index: number) => {
if (!userInput[index]) return "";
return char === userInput[index]
? "text-green-500"
: "text-red-500 underline";
};
const mistakes = sampleText
.slice(0, userInput.length)
.split("")
.filter((char, idx) => char !== userInput[idx]).length;
const wordsTyped = userInput.length / 5; // Common WPM definition: avg 5 chars per word
const wpmRaw = timeElapsed > 0 ? (wordsTyped / timeElapsed) * 60 : 0;
const wpmAdjusted =
timeElapsed > 0 ? ((wordsTyped - mistakes) / timeElapsed) * 60 : 0;
return (
<main className="min-h-screen flex flex-col items-center justify-center bg-gray-900 text-white font-mono gap-4 p-4">
<h1 className="text-3xl font-bold mb-4">Typing Test</h1>
<div className="max-w-xl">
<p className="text-xl border border-gray-700 rounded p-4 select-none">
{sampleText.split("").map((char, idx) => (
<span key={idx} className={getCharClass(char, idx)}>
{char}
</span>
))}
</p>
</div>
<input
ref={inputRef}
value={userInput}
onChange={handleChange}
className="max-w-xl w-full rounded p-3 text-lg bg-gray-800 text-white caret-white outline-none"
placeholder="Start typing here..."
/>
<div className="flex gap-6 text-lg">
<p>Time: {Math.floor(timeElapsed)}s</p>
<p>Mistakes: {mistakes}</p>
<p>Raw WPM: {Math.max(0, wpmRaw).toFixed(1)}</p>
<p>Adjusted WPM: {Math.max(0, wpmAdjusted).toFixed(1)}</p>
</div>
<button
onClick={handleReset}
className="mt-4 px-6 py-2 bg-blue-600 hover:bg-blue-700 rounded-md transition-colors"
>
Reset
</button>
</main>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment