| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Meme Typing Speed Test</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <style> |
| @import url('https://fonts.googleapis.com/css2?family=Comic+Neue:wght@400;700&display=swap'); |
| |
| body { |
| font-family: 'Comic Neue', cursive; |
| transition: background-image 0.5s ease-in-out; |
| background-size: cover; |
| background-position: center; |
| background-repeat: no-repeat; |
| min-height: 100vh; |
| } |
| |
| .bg-overlay { |
| background-color: rgba(0, 0, 0, 0.6); |
| } |
| |
| .text-display { |
| font-size: 1.2rem; |
| line-height: 1.8; |
| letter-spacing: 0.05em; |
| } |
| |
| .correct { |
| color: #4ade80; |
| } |
| |
| .incorrect { |
| color: #f87171; |
| text-decoration: underline; |
| } |
| |
| .current { |
| background-color: rgba(74, 222, 128, 0.3); |
| border-left: 2px solid #4ade80; |
| } |
| |
| .fade-in { |
| animation: fadeIn 0.5s; |
| } |
| |
| @keyframes fadeIn { |
| from { opacity: 0; } |
| to { opacity: 1; } |
| } |
| |
| .meme-caption { |
| text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8); |
| animation: bounce 1s infinite alternate; |
| } |
| |
| @keyframes bounce { |
| from { transform: translateY(0); } |
| to { transform: translateY(-5px); } |
| } |
| |
| .result-card { |
| backdrop-filter: blur(10px); |
| background-color: rgba(255, 255, 255, 0.15); |
| } |
| </style> |
| </head> |
| <body class="bg-gray-900 text-white" id="app"> |
| <div class="bg-overlay min-h-screen flex flex-col items-center justify-center p-4 md:p-8"> |
| <div class="w-full max-w-4xl mx-auto"> |
| |
| <header class="text-center mb-8 fade-in"> |
| <h1 class="text-4xl md:text-5xl font-bold mb-2 text-yellow-300">Meme Typing Speed Test</h1> |
| <p class="text-lg md:text-xl text-gray-300">Type fast, get roasted, enjoy the memes!</p> |
| </header> |
| |
| |
| <div class="flex justify-between mb-6 bg-gray-800 rounded-lg p-4 shadow-lg"> |
| <div class="text-center"> |
| <p class="text-sm text-gray-400">Speed</p> |
| <p class="text-2xl font-bold" id="wpm">0</p> |
| <p class="text-xs">WPM</p> |
| </div> |
| <div class="text-center"> |
| <p class="text-sm text-gray-400">Accuracy</p> |
| <p class="text-2xl font-bold" id="accuracy">100</p> |
| <p class="text-xs">%</p> |
| </div> |
| <div class="text-center"> |
| <p class="text-sm text-gray-400">Time</p> |
| <p class="text-2xl font-bold" id="time">60</p> |
| <p class="text-xs">seconds</p> |
| </div> |
| </div> |
| |
| |
| <div class="text-center mb-6"> |
| <p class="text-xl md:text-2xl font-bold meme-caption text-yellow-300" id="meme-caption">Ready to get roasted by your typing speed?</p> |
| </div> |
| |
| |
| <div id="test-container" class="fade-in"> |
| <div class="mb-6 p-4 bg-gray-800 rounded-lg shadow-lg"> |
| <div id="text-display" class="text-display mb-4 h-24 overflow-y-auto"></div> |
| <input |
| type="text" |
| id="input-field" |
| class="w-full p-3 bg-gray-700 rounded-lg focus:outline-none focus:ring-2 focus:ring-yellow-400" |
| placeholder="Click here to start typing..." |
| autocomplete="off" |
| autocorrect="off" |
| autocapitalize="off" |
| spellcheck="false" |
| > |
| </div> |
| |
| <div class="text-center"> |
| <button id="start-btn" class="px-6 py-3 bg-yellow-400 hover:bg-yellow-500 text-gray-900 font-bold rounded-lg transition duration-200"> |
| Start Test |
| </button> |
| </div> |
| </div> |
| |
| |
| <div id="results-container" class="hidden fade-in"> |
| <div class="result-card rounded-lg p-6 mb-6 shadow-xl border border-gray-700"> |
| <h2 class="text-3xl font-bold text-center mb-4 text-yellow-300">Your Results</h2> |
| |
| <div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6"> |
| <div class="bg-gray-800 p-4 rounded-lg"> |
| <p class="text-sm text-gray-400">Final Speed</p> |
| <p class="text-3xl font-bold" id="final-wpm">0</p> |
| <p class="text-xs">Words Per Minute</p> |
| </div> |
| <div class="bg-gray-800 p-4 rounded-lg"> |
| <p class="text-sm text-gray-400">Accuracy</p> |
| <p class="text-3xl font-bold" id="final-accuracy">100</p> |
| <p class="text-xs">% Correct</p> |
| </div> |
| <div class="bg-gray-800 p-4 rounded-lg"> |
| <p class="text-sm text-gray-400">Meme Level</p> |
| <p class="text-3xl font-bold" id="meme-level">Snail</p> |
| <p class="text-xs">Speed Tier</p> |
| </div> |
| </div> |
| |
| <div class="text-center mb-6"> |
| <p class="text-xl font-bold" id="final-caption">You type like a sloth on a lazy Sunday!</p> |
| </div> |
| |
| <div class="flex flex-col md:flex-row justify-center gap-4"> |
| <button id="restart-btn" class="px-6 py-3 bg-yellow-400 hover:bg-yellow-500 text-gray-900 font-bold rounded-lg transition duration-200"> |
| Try Again |
| </button> |
| <button id="share-btn" class="px-6 py-3 bg-blue-500 hover:bg-blue-600 text-white font-bold rounded-lg transition duration-200"> |
| Share Results |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| |
| const sampleTexts = [ |
| "The quick brown fox jumps over the lazy dog. Pack my box with five dozen liquor jugs. How vexingly quick daft zebras jump!", |
| "Crazy Fredrick bought many very exquisite opal jewels. The five boxing wizards jump quickly. My girl wove six dozen plaid jackets before she quit.", |
| "Jinxed wizards pluck ivy from the big quilt. Crazy Fredrick bought many very exquisite opal jewels. Sphinx of black quartz, judge my vow.", |
| "The quick onyx goblin jumps over the lazy dwarf. Pack my red box with five dozen quality jugs. How quickly daft jumping zebras vex!", |
| "Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim. Two driven jocks help fax my big quiz. Five quacking zephyrs jolt my wax bed." |
| ]; |
| |
| |
| const memeGifs = { |
| slow: "https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExcGJhZ2J0dG9uY2J0d3F4dGJ5d3R6Z2V4eXJ2bGJ5b2VtZ2J5eW1rZyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/3o7TKTD1P5JCDoQxKU/giphy.gif", |
| medium: "https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExb2V4dG5oZ2R4dW5tZ2J5eW1rZyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/l0HU20BZ6LbSEITza/giphy.gif", |
| fast: "https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExb2V4dG5oZ2R4dW5tZ2J5eW1rZyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/3o7aCTPPm4OHfRLSH6/giphy.gif", |
| ultra: "https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExb2V4dG5oZ2R4dW5tZ2J5eW1rZyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/xT5LMHxhOfscxPfIfm/giphy.gif" |
| }; |
| |
| |
| const memeCaptions = { |
| slow: [ |
| "Your grandma types faster than this.", |
| "Are you typing with your nose?", |
| "I've seen sloths move faster.", |
| "Is your keyboard made of molasses?", |
| "At this rate, you'll finish by next Christmas." |
| ], |
| medium: [ |
| "Not bad... for a turtle.", |
| "You're trying, and that's what counts.", |
| "Speed: Average. Meme potential: High.", |
| "You type like a middle manager.", |
| "Could be worse... could be better." |
| ], |
| fast: [ |
| "WOOOSH! You're going places!", |
| "Someone's been practicing!", |
| "Now we're cooking with gas!", |
| "Look at you go, speed demon!", |
| "The keyboard is on fire!" |
| ], |
| ultra: [ |
| "HOLY MOLY! Are you a robot?", |
| "The Flash called, he wants his speed back.", |
| "You type faster than light travels.", |
| "Did you sell your soul for typing powers?", |
| "NASA wants to study your fingers." |
| ] |
| }; |
| |
| |
| const resultCaptions = { |
| slow: [ |
| "You type like a sloth on a lazy Sunday.", |
| "Your fingers move at geological speeds.", |
| "At least you're consistent... consistently slow.", |
| "Maybe try voice typing next time?", |
| "The good news: You can only improve from here!" |
| ], |
| medium: [ |
| "You're the typing equivalent of vanilla ice cream.", |
| "Not too hot, not too cold... just right.", |
| "You type like a reliable Honda Civic.", |
| "Perfectly adequate in every way.", |
| "You're the human equivalent of 60 WPM." |
| ], |
| fast: [ |
| "Look at you, you speedy little typist!", |
| "Your fingers dance across the keyboard!", |
| "You type like a caffeinated secretary.", |
| "The keyboard trembles at your approach!", |
| "You've clearly spent too much time online." |
| ], |
| ultra: [ |
| "ARE YOU EVEN HUMAN? This is insane!", |
| "Your fingers are a blur of typing fury!", |
| "The keyboard can't keep up with you!", |
| "You type faster than most people think!", |
| "Warning: Typing at this speed may cause spontaneous combustion." |
| ] |
| }; |
| |
| |
| const textDisplay = document.getElementById('text-display'); |
| const inputField = document.getElementById('input-field'); |
| const startBtn = document.getElementById('start-btn'); |
| const wpmDisplay = document.getElementById('wpm'); |
| const accuracyDisplay = document.getElementById('accuracy'); |
| const timeDisplay = document.getElementById('time'); |
| const memeCaption = document.getElementById('meme-caption'); |
| const testContainer = document.getElementById('test-container'); |
| const resultsContainer = document.getElementById('results-container'); |
| const finalWpm = document.getElementById('final-wpm'); |
| const finalAccuracy = document.getElementById('final-accuracy'); |
| const memeLevel = document.getElementById('meme-level'); |
| const finalCaption = document.getElementById('final-caption'); |
| const restartBtn = document.getElementById('restart-btn'); |
| const shareBtn = document.getElementById('share-btn'); |
| |
| |
| let currentText = ''; |
| let timer; |
| let timeLeft = 60; |
| let isTyping = false; |
| let correctChars = 0; |
| let incorrectChars = 0; |
| let totalTyped = 0; |
| let wpm = 0; |
| let accuracy = 100; |
| let speedInterval; |
| let lastUpdateTime = 0; |
| let correctKeystrokes = 0; |
| let totalKeystrokes = 0; |
| |
| |
| function init() { |
| |
| preloadGifs(); |
| |
| |
| startBtn.addEventListener('click', startTest); |
| inputField.addEventListener('input', checkTyping); |
| inputField.addEventListener('keydown', (e) => { |
| if (e.key === 'Enter') { |
| e.preventDefault(); |
| } |
| }); |
| restartBtn.addEventListener('click', resetTest); |
| shareBtn.addEventListener('click', shareResults); |
| |
| |
| displaySampleText(); |
| } |
| |
| |
| function preloadGifs() { |
| Object.values(memeGifs).forEach(url => { |
| const img = new Image(); |
| img.src = url; |
| }); |
| } |
| |
| |
| function displaySampleText() { |
| currentText = sampleTexts[Math.floor(Math.random() * sampleTexts.length)]; |
| textDisplay.innerHTML = ''; |
| |
| currentText.split('').forEach(char => { |
| const span = document.createElement('span'); |
| span.textContent = char; |
| textDisplay.appendChild(span); |
| }); |
| } |
| |
| |
| function startTest() { |
| if (isTyping) return; |
| |
| isTyping = true; |
| timeLeft = 60; |
| correctChars = 0; |
| incorrectChars = 0; |
| totalTyped = 0; |
| wpm = 0; |
| accuracy = 100; |
| correctKeystrokes = 0; |
| totalKeystrokes = 0; |
| |
| |
| startBtn.classList.add('hidden'); |
| inputField.value = ''; |
| inputField.focus(); |
| timeDisplay.textContent = timeLeft; |
| |
| |
| timer = setInterval(updateTimer, 1000); |
| |
| |
| lastUpdateTime = Date.now(); |
| speedInterval = setInterval(calculateSpeed, 2000); |
| |
| |
| updateMeme(); |
| } |
| |
| |
| function updateTimer() { |
| timeLeft--; |
| timeDisplay.textContent = timeLeft; |
| |
| if (timeLeft <= 0) { |
| finishTest(); |
| } |
| } |
| |
| |
| function calculateSpeed() { |
| const now = Date.now(); |
| const timeElapsed = (now - lastUpdateTime) / 1000 / 60; |
| const charsTyped = totalTyped; |
| |
| if (timeElapsed > 0) { |
| |
| wpm = Math.round((correctChars / 5) / timeElapsed); |
| accuracy = totalTyped > 0 ? Math.round((correctKeystrokes / totalKeystrokes) * 100) : 100; |
| |
| wpmDisplay.textContent = wpm; |
| accuracyDisplay.textContent = accuracy; |
| |
| updateMeme(); |
| } |
| |
| lastUpdateTime = now; |
| correctChars = 0; |
| totalTyped = 0; |
| } |
| |
| |
| function checkTyping() { |
| if (!isTyping) return; |
| |
| const inputText = inputField.value; |
| const currentChar = inputText[inputText.length - 1]; |
| const expectedChar = currentText[inputText.length - 1]; |
| |
| totalTyped++; |
| totalKeystrokes++; |
| |
| |
| if (currentChar === expectedChar) { |
| correctChars++; |
| correctKeystrokes++; |
| } else { |
| incorrectChars++; |
| } |
| |
| |
| updateTextDisplay(inputText); |
| } |
| |
| |
| function updateTextDisplay(inputText) { |
| const textSpans = textDisplay.querySelectorAll('span'); |
| |
| textSpans.forEach((span, index) => { |
| |
| span.className = ''; |
| |
| |
| if (index < inputText.length) { |
| if (inputText[index] === currentText[index]) { |
| span.classList.add('correct'); |
| } else { |
| span.classList.add('incorrect'); |
| } |
| } |
| |
| |
| if (index === inputText.length) { |
| span.classList.add('current'); |
| } |
| }); |
| |
| |
| const currentSpan = textDisplay.querySelector('.current'); |
| if (currentSpan) { |
| currentSpan.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); |
| } |
| } |
| |
| |
| function updateMeme() { |
| let speedTier, randomCaption; |
| |
| if (wpm <= 20) { |
| speedTier = 'slow'; |
| randomCaption = memeCaptions.slow[Math.floor(Math.random() * memeCaptions.slow.length)]; |
| } else if (wpm <= 50) { |
| speedTier = 'medium'; |
| randomCaption = memeCaptions.medium[Math.floor(Math.random() * memeCaptions.medium.length)]; |
| } else if (wpm <= 80) { |
| speedTier = 'fast'; |
| randomCaption = memeCaptions.fast[Math.floor(Math.random() * memeCaptions.fast.length)]; |
| } else { |
| speedTier = 'ultra'; |
| randomCaption = memeCaptions.ultra[Math.floor(Math.random() * memeCaptions.ultra.length)]; |
| } |
| |
| |
| document.body.style.backgroundImage = `url(${memeGifs[speedTier]})`; |
| |
| |
| memeCaption.textContent = randomCaption; |
| } |
| |
| |
| function finishTest() { |
| clearInterval(timer); |
| clearInterval(speedInterval); |
| isTyping = false; |
| |
| |
| const finalWpmValue = Math.max(0, wpm); |
| const finalAccuracyValue = Math.max(0, Math.min(100, accuracy)); |
| |
| |
| let speedTier, levelName, randomResultCaption; |
| |
| if (finalWpmValue <= 20) { |
| speedTier = 'slow'; |
| levelName = 'Snail'; |
| randomResultCaption = resultCaptions.slow[Math.floor(Math.random() * resultCaptions.slow.length)]; |
| } else if (finalWpmValue <= 50) { |
| speedTier = 'medium'; |
| levelName = 'Average Joe'; |
| randomResultCaption = resultCaptions.medium[Math.floor(Math.random() * resultCaptions.medium.length)]; |
| } else if (finalWpmValue <= 80) { |
| speedTier = 'fast'; |
| levelName = 'Speed Demon'; |
| randomResultCaption = resultCaptions.fast[Math.floor(Math.random() * resultCaptions.fast.length)]; |
| } else { |
| speedTier = 'ultra'; |
| levelName = 'GOD MODE'; |
| randomResultCaption = resultCaptions.ultra[Math.floor(Math.random() * resultCaptions.ultra.length)]; |
| } |
| |
| |
| finalWpm.textContent = finalWpmValue; |
| finalAccuracy.textContent = finalAccuracyValue; |
| memeLevel.textContent = levelName; |
| finalCaption.textContent = randomResultCaption; |
| |
| |
| document.body.style.backgroundImage = `url(${memeGifs[speedTier]})`; |
| |
| |
| testContainer.classList.add('hidden'); |
| resultsContainer.classList.remove('hidden'); |
| } |
| |
| |
| function resetTest() { |
| clearInterval(timer); |
| clearInterval(speedInterval); |
| |
| isTyping = false; |
| inputField.value = ''; |
| |
| |
| wpmDisplay.textContent = '0'; |
| accuracyDisplay.textContent = '100'; |
| timeDisplay.textContent = '60'; |
| |
| |
| testContainer.classList.remove('hidden'); |
| resultsContainer.classList.add('hidden'); |
| |
| |
| startBtn.classList.remove('hidden'); |
| |
| |
| displaySampleText(); |
| |
| |
| memeCaption.textContent = "Ready to get roasted by your typing speed?"; |
| document.body.style.backgroundImage = ''; |
| } |
| |
| |
| function shareResults() { |
| const finalWpmValue = finalWpm.textContent; |
| const finalAccuracyValue = finalAccuracy.textContent; |
| const levelName = memeLevel.textContent; |
| const caption = finalCaption.textContent; |
| |
| const shareText = `I just scored ${finalWpmValue} WPM (${finalAccuracyValue}% accuracy) on the Meme Typing Speed Test! ${caption} Try it yourself!`; |
| |
| if (navigator.share) { |
| navigator.share({ |
| title: 'My Typing Speed Results', |
| text: shareText, |
| url: window.location.href |
| }).catch(err => { |
| console.log('Error sharing:', err); |
| fallbackShare(shareText); |
| }); |
| } else { |
| fallbackShare(shareText); |
| } |
| } |
| |
| |
| function fallbackShare(text) { |
| |
| navigator.clipboard.writeText(text).then(() => { |
| alert('Results copied to clipboard! Paste it anywhere to share.'); |
| }).catch(err => { |
| console.log('Could not copy text: ', err); |
| prompt('Copy this to share:', text); |
| }); |
| } |
| |
| |
| document.addEventListener('DOMContentLoaded', init); |
| </script> |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=GameDev102/meme-typing-speed-test" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| </html> |