├── 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 |
--------------------------------------------------------------------------------