Test Your Typing Speed in 60 Seconds

Check your WPM and accuracy instantly.

Customize Your Test
1 Minute Test
15 Seconds Test
30 Seconds Test
1 Minute Test
2 Minutes Test
5 Minutes Test
10 Minutes Test
15 Minutes Test
30 Minutes Test
1 Hour Test
Medium Text
Easy Text
Medium Text
Hard Text
Default Mode (Random Paragraph)
Default Mode (Random Paragraph)
Alice in Wonderland (Classic Story)
Sherlock Holmes (Mystery & Investigation)
Albert Einstein (Imagination & Science)
Steve Jobs (Stay Hungry, Stay Foolish)
Dark Mode
⏱️ 1:00
15 Seconds
30 Seconds
1 Minute
2 Minutes
5 Minutes
10 Minutes
15 Minutes
30 Minutes
1 Hour
MODE
0
Your Test Score
0
Typing Speed
×
0%
0 typos
=
0
Net Speed
Certificate of Achievement
Typing Speed Proficiency
This certificate is proudly presented to
TypeMaster Champion
0 min
Test Time
0 WPM
Typing Speed
0%
Accuracy
0
Mistakes

Test Performance Summary

Typed Paragraph Content:

Mistakes Log:

None

Timeline Graph:

Verified & Issued by TypeMaster Platform
⚠️ Mistakes Made During Typing
No mistakes found. Excellent typing!
Performance Timeline (WPM)
'); printWindow.document.close(); printWindow.focus(); setTimeout(() => { printWindow.print(); printWindow.close(); }, 500); }); function startTest() { timeLeft = selectedTime; let textKey = (selectedFamous !== 'default') ? selectedFamous : selectedDiff; currentText = getUnlimitedText(textKey); setupScreen.classList.remove('active'); resultsScreen.classList.remove('active'); testScreen.classList.add('active'); mainContainer.classList.add('test-running'); const mins = Math.floor(selectedTime / 60); const secs = selectedTime % 60; timerTriggerText.textContent = `⏱️ ${mins}:${secs < 10 ? '0' : ''}${secs}`; textDisplay.innerHTML = currentText.split('').map((char, index) => `${char}`).join(''); textDisplay.style.transform = "scale(1)"; textDisplay.style.color = ""; charIndex = 0; typos = 0; correctChars = 0; isStarted = false; previousInputLength = 0; mistakesLog = []; liveWpmDisplay.textContent = "0"; pulseDot.classList.remove('active'); proInputDisplay.textContent = ""; wpmHistory = [0]; clearCanvasHD(canvas, ctx); updateTimerDisplay(); hiddenInput.value = ""; hiddenInput.focus(); } textDisplay.addEventListener('click', () => { initAudio(); hiddenInput.focus(); }); proInputDisplay.addEventListener('click', () => { initAudio(); hiddenInput.focus(); }); hiddenInput.addEventListener('input', () => { initAudio(); const charSpans = textDisplay.querySelectorAll('span'); const typedValue = hiddenInput.value; const currentCharPos = typedValue.length - 1; proInputDisplay.textContent = typedValue; if (!isStarted && typedValue.length > 0) { isStarted = true; pulseDot.classList.add('active'); startTimer(); } if (currentCharPos >= currentText.length) return; if (typedValue.length < previousInputLength) { for (let i = currentCharPos + 1; i <= charIndex; i++) { if (i < charSpans.length) charSpans[i].classList.remove('correct', 'incorrect'); } charIndex = currentCharPos + 1; playTypingSound(); } else { const typedChar = typedValue[currentCharPos]; const targetChar = currentText[currentCharPos]; if (typedChar === targetChar) { charSpans[currentCharPos].classList.add('correct'); correctChars++; playTypingSound(); } else { charSpans[currentCharPos].classList.add('incorrect'); typos++; mistakesLog.push({ typed: typedChar === ' ' ? '[space]' : typedChar, expected: targetChar === ' ' ? '[space]' : targetChar, pos: currentCharPos + 1 }); playErrorSound(); } charIndex = currentCharPos + 1; } previousInputLength = typedValue.length; charSpans.forEach(span => span.classList.remove('current')); if (charIndex < charSpans.length) charSpans[charIndex].classList.add('current'); if (charIndex >= currentText.length) endTest(); }); function startTimer() { clearInterval(timer); timer = setInterval(() => { if (timeLeft > 0) { timeLeft--; updateTimerDisplay(); calculateLiveWPM(); } else { endTest(); } }, 1000); } function updateTimerDisplay() { const mins = Math.floor(timeLeft / 60); const secs = timeLeft % 60; timerTriggerText.textContent = `⏱️ ${mins}:${secs < 10 ? '0' : ''}${secs}`; } function calculateLiveWPM() { const timeSpentMinutes = (selectedTime - timeLeft) / 60; if (timeSpentMinutes > 0) { const totalTyped = charIndex || 1; const liveGrossWPM = Math.round((totalTyped / 5) / timeSpentMinutes); const liveAccuracy = Math.max(0, Math.round(((totalTyped - typos) / totalTyped) * 100)); const liveNetWPM = Math.max(0, Math.round(liveGrossWPM * (liveAccuracy / 100))); liveWpmDisplay.textContent = liveNetWPM; wpmHistory.push(liveNetWPM); drawLiveChart(); updateSpeedVisuals(liveNetWPM); } } function updateSpeedVisuals(wpm) { let scaleFactor = 1; let dynamicColor = ""; if (wpm < 20) { dynamicColor = document.body.classList.contains('dark-mode') ? '#a8a29e' : '#78716c'; scaleFactor = 1.0; } else if (wpm >= 20 && wpm < 30) { dynamicColor = '#e11d48'; scaleFactor = 1.01; } else if (wpm >= 30 && wpm < 40) { dynamicColor = '#f59e0b'; scaleFactor = 1.02; } else if (wpm >= 40 && wpm < 50) { dynamicColor = '#3b82f6'; scaleFactor = 1.03; } else if (wpm >= 50 && wpm < 60) { dynamicColor = '#10b981'; scaleFactor = 1.04; } else { dynamicColor = '#8b5cf6'; scaleFactor = 1.05; } textDisplay.style.color = dynamicColor; textDisplay.style.transform = `scale(${scaleFactor})`; } function prepareHDCanvas(cnv, context) { const dpr = window.devicePixelRatio || 1; const displayWidth = cnv.clientWidth || parseInt(cnv.style.width) || cnv.width; const displayHeight = cnv.clientHeight || parseInt(cnv.style.height) || cnv.height; if (cnv.width !== displayWidth * dpr || cnv.height !== displayHeight * dpr) { cnv.width = displayWidth * dpr; cnv.height = displayHeight * dpr; } context.setTransform(1, 0, 0, 1, 0, 0); context.scale(dpr, dpr); return { width: displayWidth, height: displayHeight }; } function clearCanvasHD(cnv, context) { const dpr = window.devicePixelRatio || 1; context.clearRect(0, 0, cnv.width / dpr, cnv.height / dpr); } function drawLiveChart() { if (wpmHistory.length === 0) return; const dims = prepareHDCanvas(canvas, ctx); const isDarkMode = document.body.classList.contains('dark-mode'); const primaryColor = isDarkMode ? '#e07a5f' : '#d97757'; clearCanvasHD(canvas, ctx); const padX = 5; const padY = 5; const width = dims.width - padX * 2; const height = dims.height - padY * 2; const maxWpm = Math.max(50, ...wpmHistory) + 10; ctx.beginPath(); ctx.strokeStyle = primaryColor; ctx.lineWidth = 2.5; ctx.lineJoin = 'round'; ctx.lineCap = 'round'; ctx.shadowBlur = 4; ctx.shadowColor = primaryColor; for (let i = 0; i < wpmHistory.length; i++) { let progressX = i / Math.max(1, selectedTime); let x = padX + (progressX * width); let y = padY + height - (wpmHistory[i] / maxWpm) * height; if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y); } ctx.stroke(); ctx.shadowBlur = 0; } function drawFinalChart() { finalCanvas.width = finalCanvas.parentElement.clientWidth - 30; finalCanvas.height = 125; const dims = prepareHDCanvas(finalCanvas, ctxFinal); ctxFinal.clearRect(0, 0, dims.width, dims.height); if (wpmHistory.length === 0) return; const isDarkMode = document.body.classList.contains('dark-mode'); const primaryColor = isDarkMode ? '#e07a5f' : '#d97757'; const gradientColor = isDarkMode ? 'rgba(224, 122, 95, 0.25)' : 'rgba(217, 119, 87, 0.25)'; const textColor = isDarkMode ? '#a8a29e' : '#78716c'; const padX = 15; const padY = 20; const width = dims.width - padX * 2; const height = dims.height - padY * 2; const maxWpm = Math.max(50, ...wpmHistory) + 10; let gradient = ctxFinal.createLinearGradient(0, padY, 0, dims.height - padY); gradient.addColorStop(0, gradientColor); gradient.addColorStop(1, 'rgba(0,0,0,0)'); ctxFinal.beginPath(); ctxFinal.moveTo(padX, padY + height); for (let i = 0; i < wpmHistory.length; i++) { let progressX = i / Math.max(1, wpmHistory.length - 1); let x = padX + (progressX * width); let y = padY + height - (wpmHistory[i] / maxWpm) * height; ctxFinal.lineTo(x, y); } ctxFinal.lineTo(padX + width, padY + height); ctxFinal.closePath(); ctxFinal.fillStyle = gradient; ctxFinal.fill(); ctxFinal.beginPath(); ctxFinal.strokeStyle = primaryColor; ctxFinal.lineWidth = 3; ctxFinal.lineJoin = 'round'; ctxFinal.lineCap = 'round'; for (let i = 0; i < wpmHistory.length; i++) { let progressX = i / Math.max(1, wpmHistory.length - 1); let x = padX + (progressX * width); let y = padY + height - (wpmHistory[i] / maxWpm) * height; if (i === 0) ctxFinal.moveTo(x, y); else ctxFinal.lineTo(x, y); } ctxFinal.stroke(); ctxFinal.fillStyle = primaryColor; ctxFinal.font = 'bold 11px "Plus Jakarta Sans"'; ctxFinal.textAlign = 'center'; for (let i = 0; i < wpmHistory.length; i++) { let progressX = i / Math.max(1, wpmHistory.length - 1); let x = padX + (progressX * width); let y = padY + height - (wpmHistory[i] / maxWpm) * height; ctxFinal.beginPath(); ctxFinal.arc(x, y, 3.5, 0, Math.PI * 2); ctxFinal.fill(); if (i === 0 || i === wpmHistory.length - 1 || i % Math.ceil(wpmHistory.length / 8) === 0) { ctxFinal.fillStyle = textColor; ctxFinal.fillText(wpmHistory[i], x, y - 8); ctxFinal.fillStyle = primaryColor; } } } function endTest() { clearInterval(timer); pulseDot.classList.remove('active'); testScreen.classList.remove('active'); resultsScreen.classList.add('active'); // Reset certificate button views on new result viewCertBtn.style.display = 'block'; downloadActionGroup.style.display = 'none'; document.getElementById('certificate-template').style.display = 'none'; const timeSpentMinutes = (selectedTime - timeLeft) / 60 || (selectedTime / 60); const totalTyped = charIndex || 1; const grossWPM = Math.round((totalTyped / 5) / timeSpentMinutes); const accuracy = Math.max(0, Math.round(((totalTyped - typos) / totalTyped) * 100)); const netWPM = Math.max(0, Math.round(grossWPM * (accuracy / 100))); document.getElementById('res-wpm').textContent = grossWPM; document.getElementById('res-accuracy').textContent = `${accuracy}%`; document.getElementById('res-typos').textContent = typos; document.getElementById('res-net').textContent = netWPM; const mistakesContainer = document.getElementById('mistakes-list-container'); if (mistakesLog.length === 0) { mistakesContainer.innerHTML = "No mistakes found. Excellent typing!"; } else { let mistakesHTML = ""; let uniqueMistakes = {}; mistakesLog.forEach(m => { let key = `Typed ${m.typed} instead of ${m.expected}`; uniqueMistakes[key] = (uniqueMistakes[key] || 0) + 1; }); for (let err in uniqueMistakes) { mistakesHTML += `
• ${err} (${uniqueMistakes[err]}x)
`; } mistakesContainer.innerHTML = mistakesHTML; } setTimeout(drawFinalChart, 50); } function resetToSetup() { clearInterval(timer); pulseDot.classList.remove('active'); mainContainer.classList.remove('test-running'); testScreen.classList.remove('active'); resultsScreen.classList.remove('active'); setupScreen.classList.add('active'); }