├── README.md ├── index.html ├── script.js └── style.css /README.md: -------------------------------------------------------------------------------- 1 | # ⌨️ Typing Speed Test 2 | 3 | A sleek typing speed test tool with animated background, WPM calculation, and real-time typing feedback. 4 | 5 | ## 🚀 Features 6 | - Animated glowing background 7 | - Calculates WPM in real-time 8 | - Highlights correct and incorrect letters 9 | - Timer starts on first keystroke 10 | - Mobile-friendly design 11 | 12 | ## 🛠 Tech Stack 13 | - HTML 14 | - CSS (Flexbox + animation) 15 | - JavaScript 16 | 17 | ## 📸 Screenshots 18 | _Add screenshots here of the app running_ 19 | 20 | ## 📦 How to Run 21 | 1. Clone this repository: 22 | ```bash 23 | git clone https://github.com/yourusername/typing-speed-test.git 24 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Typing Speed Test 7 | 8 | 9 | 10 |
11 |
12 |

⌨️ Typing Speed Test

13 |
14 | 15 |
16 |

Time: 0 sec

17 |

WPM: 0

18 |
19 | 20 |
21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /script.js: -------------------------------------------------------------------------------- 1 | const quoteDisplay = document.getElementById("quoteDisplay"); 2 | const quoteInput = document.getElementById("quoteInput"); 3 | const timerElement = document.getElementById("timer"); 4 | const wpmElement = document.getElementById("wpm"); 5 | const restartBtn = document.getElementById("restartBtn"); 6 | 7 | let startTime, timerInterval; 8 | 9 | const sampleQuotes = [ 10 | "Typing fast requires practice and precision.", 11 | "Front-end development includes HTML, CSS, and JavaScript.", 12 | "You can achieve great things with consistent effort.", 13 | "Creativity and code go hand in hand in web design.", 14 | "Build projects to sharpen your programming skills." 15 | ]; 16 | 17 | function getRandomQuote() { 18 | return sampleQuotes[Math.floor(Math.random() * sampleQuotes.length)]; 19 | } 20 | 21 | function displayNewQuote() { 22 | const quote = getRandomQuote(); 23 | quoteDisplay.innerHTML = ""; 24 | quote.split("").forEach(char => { 25 | const span = document.createElement("span"); 26 | span.innerText = char; 27 | quoteDisplay.appendChild(span); 28 | }); 29 | quoteInput.value = ""; 30 | resetTimer(); 31 | } 32 | 33 | function resetTimer() { 34 | clearInterval(timerInterval); 35 | timerElement.textContent = "0"; 36 | wpmElement.textContent = "0"; 37 | startTime = null; 38 | } 39 | 40 | quoteInput.addEventListener("input", () => { 41 | const quoteSpans = quoteDisplay.querySelectorAll("span"); 42 | const inputChars = quoteInput.value.split(""); 43 | 44 | if (!startTime) { 45 | startTime = new Date(); 46 | timerInterval = setInterval(() => { 47 | const elapsed = Math.floor((new Date() - startTime) / 1000); 48 | timerElement.textContent = elapsed; 49 | const words = inputChars.length / 5; 50 | const wpm = Math.round((words / elapsed) * 60) || 0; 51 | wpmElement.textContent = wpm; 52 | }, 1000); 53 | } 54 | 55 | let correct = true; 56 | quoteSpans.forEach((span, index) => { 57 | const char = inputChars[index]; 58 | if (char == null) { 59 | span.classList.remove("correct", "incorrect"); 60 | correct = false; 61 | } else if (char === span.innerText) { 62 | span.classList.add("correct"); 63 | span.classList.remove("incorrect"); 64 | } else { 65 | span.classList.add("incorrect"); 66 | span.classList.remove("correct"); 67 | correct = false; 68 | } 69 | }); 70 | 71 | if (correct && inputChars.length === quoteSpans.length) { 72 | clearInterval(timerInterval); 73 | } 74 | }); 75 | 76 | restartBtn.addEventListener("click", displayNewQuote); 77 | 78 | // Start 79 | displayNewQuote(); 80 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | /* Reset & Base Styles */ 2 | * { 3 | margin: 0; 4 | padding: 0; 5 | box-sizing: border-box; 6 | font-family: 'Segoe UI', sans-serif; 7 | } 8 | body { 9 | background: #0f2027; /* fallback for old browsers */ 10 | background: linear-gradient(to right, #2c5364, #203a43, #0f2027); 11 | min-height: 100vh; 12 | color: #fff; 13 | display: flex; 14 | justify-content: center; 15 | align-items: center; 16 | position: relative; 17 | overflow: hidden; 18 | } 19 | 20 | /* Glowing Circle Animation */ 21 | .background::before { 22 | content: ''; 23 | position: absolute; 24 | width: 500px; 25 | height: 500px; 26 | border-radius: 50%; 27 | background: radial-gradient(circle, #00f2fe, #4facfe); 28 | top: -100px; 29 | left: -100px; 30 | animation: pulse 6s ease-in-out infinite; 31 | opacity: 0.3; 32 | z-index: 0; 33 | } 34 | 35 | @keyframes pulse { 36 | 0%, 100% { transform: scale(1); } 37 | 50% { transform: scale(1.3); } 38 | } 39 | 40 | .container { 41 | background-color: rgba(255, 255, 255, 0.05); 42 | padding: 30px; 43 | border-radius: 15px; 44 | box-shadow: 0 8px 30px rgba(0, 0, 0, 0.5); 45 | max-width: 700px; 46 | width: 90%; 47 | text-align: center; 48 | position: relative; 49 | z-index: 1; 50 | } 51 | 52 | h1 { 53 | margin-bottom: 20px; 54 | font-size: 2rem; 55 | } 56 | 57 | .quote { 58 | font-size: 1.2rem; 59 | margin-bottom: 20px; 60 | color: #ddd; 61 | line-height: 1.6; 62 | min-height: 80px; 63 | } 64 | 65 | textarea { 66 | width: 100%; 67 | height: 100px; 68 | padding: 15px; 69 | font-size: 1rem; 70 | border-radius: 10px; 71 | border: none; 72 | outline: none; 73 | background: #111; 74 | color: #fff; 75 | resize: none; 76 | } 77 | 78 | .stats { 79 | display: flex; 80 | justify-content: space-around; 81 | margin-top: 20px; 82 | font-size: 1.1rem; 83 | } 84 | 85 | button { 86 | margin-top: 20px; 87 | padding: 10px 25px; 88 | font-size: 1rem; 89 | border: none; 90 | border-radius: 10px; 91 | background-color: #4facfe; 92 | color: white; 93 | cursor: pointer; 94 | transition: background-color 0.3s ease; 95 | } 96 | 97 | button:hover { 98 | background-color: #00c6ff; 99 | } 100 | --------------------------------------------------------------------------------