|
<!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> |