diff --git a/src/data/content.js b/src/data/content.js
index baca279..2660737 100644
--- a/src/data/content.js
+++ b/src/data/content.js
@@ -10,6 +10,7 @@ import { RandomMeme } from "../pages/activities/RandomMemes";
import { RandomJoke } from "../pages/activities/RandomJoke";
import { RandomAnimeQuote } from "../pages/activities/RandomAnimeQuote";
import { SimonSays } from "../pages/games/SimonSays";
+import { ReactionTime } from "../pages/games/ReactionTime";
import MemeCaptionMaker from "../pages/games/MemeCaptionMaker";
import meme from "../assets/activities/meme.jpg";
import dog from "../assets/activities/dogimage.jpeg";
@@ -130,6 +131,13 @@ export const games = [
urlTerm: "simon-says",
element: ,
},
+ {
+ title: "Reaction Time Test",
+ description: "Test your reflexes - click as fast as you can!",
+ icon: "https://cdn-icons-png.flaticon.com/512/2972/2972554.png",
+ urlTerm: "reaction-time",
+ element: ,
+ },
{
title: "Meme Caption Maker",
description: "Create hilarious memes with custom captions",
diff --git a/src/pages/games/ReactionTime.js b/src/pages/games/ReactionTime.js
new file mode 100644
index 0000000..fd38726
--- /dev/null
+++ b/src/pages/games/ReactionTime.js
@@ -0,0 +1,205 @@
+import React, { useState, useEffect, useRef } from 'react';
+import "../../styles/pages/games/ReactionTime.css";
+
+export const ReactionTime = () => {
+ const [gameState, setGameState] = useState('idle'); // idle, waiting, ready, result
+ const [reactionTimes, setReactionTimes] = useState([]);
+ const [currentAttempt, setCurrentAttempt] = useState(null);
+ const [message, setMessage] = useState('Click to Start');
+ const [stats, setStats] = useState({ average: 0, fastest: 0, slowest: 0 });
+ const [tooEarly, setTooEarly] = useState(false);
+
+ const startTimeRef = useRef(null);
+ const timeoutRef = useRef(null);
+
+ useEffect(() => {
+ return () => {
+ if (timeoutRef.current) {
+ clearTimeout(timeoutRef.current);
+ }
+ };
+ }, []);
+
+ useEffect(() => {
+ if (reactionTimes.length > 0) {
+ const avg = reactionTimes.reduce((a, b) => a + b, 0) / reactionTimes.length;
+ const fastest = Math.min(...reactionTimes);
+ const slowest = Math.max(...reactionTimes);
+ setStats({
+ average: Math.round(avg),
+ fastest: fastest,
+ slowest: slowest
+ });
+ }
+ }, [reactionTimes]);
+
+ const startGame = () => {
+ if (gameState === 'waiting' || gameState === 'ready') return;
+
+ setTooEarly(false);
+ setGameState('waiting');
+ setMessage('Wait for green...');
+ setCurrentAttempt(null);
+
+ // Random delay between 1000ms and 5000ms
+ const randomDelay = Math.floor(Math.random() * 4000) + 1000;
+
+ timeoutRef.current = setTimeout(() => {
+ setGameState('ready');
+ setMessage('CLICK NOW!');
+ startTimeRef.current = Date.now();
+ }, randomDelay);
+ };
+
+ const handleClick = () => {
+ if (gameState === 'idle' || gameState === 'result') {
+ startGame();
+ } else if (gameState === 'waiting') {
+ // Clicked too early
+ clearTimeout(timeoutRef.current);
+ setGameState('result');
+ setMessage('Too early! Click to try again');
+ setTooEarly(true);
+ setCurrentAttempt(null);
+ } else if (gameState === 'ready') {
+ // Calculate reaction time
+ const reactionTime = Date.now() - startTimeRef.current;
+ setCurrentAttempt(reactionTime);
+ setReactionTimes([...reactionTimes, reactionTime]);
+ setGameState('result');
+ setMessage(`${reactionTime}ms - Click to try again`);
+ setTooEarly(false);
+ }
+ };
+
+ const resetStats = () => {
+ setReactionTimes([]);
+ setStats({ average: 0, fastest: 0, slowest: 0 });
+ setGameState('idle');
+ setMessage('Click to Start');
+ setCurrentAttempt(null);
+ setTooEarly(false);
+ };
+
+ const getPerformanceRating = (time) => {
+ if (time < 200) return { text: 'Lightning Fast!', color: '#FFD700' };
+ if (time < 250) return { text: 'Excellent!', color: '#00ff41' };
+ if (time < 300) return { text: 'Great!', color: '#00d4ff' };
+ if (time < 400) return { text: 'Good!', color: '#7fff00' };
+ if (time < 500) return {text: 'Not bad!', color: '#ffaa00' };
+ return { text: 'Keep trying!', color: '#ff6b6b' };
+ };
+
+ const getStateClass = () => {
+ if (gameState === 'waiting') return 'waiting';
+ if (gameState === 'ready') return 'ready';
+ if (tooEarly) return 'too-early';
+ return '';
+ };
+
+ return (
+
+
+
⚡ Reaction Time Test
+
Test how fast your reflexes are!
+
+
+
+
+
{message}
+ {currentAttempt && !tooEarly && (
+
+ {getPerformanceRating(currentAttempt).emoji}
+ {getPerformanceRating(currentAttempt).text}
+
+ )}
+ {tooEarly && (
+
+ ❌
+ Wait for the green screen!
+
+ )}
+
+
+ {gameState === 'waiting' && (
+
+ )}
+
+
+
+
+ {gameState === 'idle' && '👆 Click the box above to start'}
+ {gameState === 'waiting' && '⏳ Wait for the screen to turn green'}
+ {gameState === 'ready' && '🎯 Click as fast as you can!'}
+ {gameState === 'result' && '🔄 Click to try again'}
+
+
+
+ {reactionTimes.length > 0 && (
+
+
📊 Your Statistics
+
+
+
📈
+
Attempts
+
{reactionTimes.length}
+
+
+
⚡
+
Average
+
{stats.average}ms
+
+
+
🏆
+
Fastest
+
{stats.fastest}ms
+
+
+
🐢
+
Slowest
+
{stats.slowest}ms
+
+
+
+
+
Recent Attempts
+
+ {reactionTimes.slice().reverse().map((time, index) => {
+ const rating = getPerformanceRating(time);
+ return (
+
+ #{reactionTimes.length - index}
+ {time}ms
+
+ {rating.emoji} {rating.text}
+
+
+ );
+ })}
+
+
+
+
+ 🔄 Reset Statistics
+
+
+ )}
+
+
+
💡 How It Works
+
+ Click the box to start the test
+ Wait for the screen to turn green
+ Click as fast as you can when it turns green
+ Try multiple times to improve your average!
+ Don't click too early or you'll have to start over
+
+
+
+ );
+};
+
diff --git a/src/styles/pages/games/ReactionTime.css b/src/styles/pages/games/ReactionTime.css
new file mode 100644
index 0000000..c74ff82
--- /dev/null
+++ b/src/styles/pages/games/ReactionTime.css
@@ -0,0 +1,513 @@
+.reaction-time-container {
+ min-height: 100vh;
+ padding: 2rem;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+}
+
+.reaction-header {
+ text-align: center;
+ margin-bottom: 2rem;
+ animation: fadeInDown 0.6s ease-out;
+}
+
+.reaction-title {
+ font-size: 3rem;
+ color: white;
+ margin: 0;
+ text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
+ font-weight: 700;
+}
+
+.reaction-subtitle {
+ font-size: 1.2rem;
+ color: rgba(255, 255, 255, 0.9);
+ margin: 0.5rem 0 0 0;
+}
+
+.reaction-game-area {
+ max-width: 800px;
+ margin: 0 auto 2rem auto;
+ min-height: 400px;
+ background-color: #3498db;
+ border-radius: 20px;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
+ position: relative;
+ overflow: hidden;
+ animation: fadeInUp 0.6s ease-out;
+}
+
+.reaction-game-area:hover {
+ transform: translateY(-5px);
+ box-shadow: 0 15px 50px rgba(0, 0, 0, 0.4);
+}
+
+.reaction-game-area.waiting {
+ background-color: #e74c3c;
+ animation: pulse 1.5s ease-in-out infinite;
+}
+
+.reaction-game-area.ready {
+ background-color: #2ecc71;
+ animation: greenFlash 0.5s ease-out;
+}
+
+.reaction-game-area.too-early {
+ background-color: #c0392b;
+ animation: shake 0.5s ease-out;
+}
+
+@keyframes pulse {
+ 0%, 100% {
+ transform: scale(1);
+ }
+ 50% {
+ transform: scale(1.02);
+ }
+}
+
+@keyframes greenFlash {
+ 0% {
+ background-color: #e74c3c;
+ }
+ 100% {
+ background-color: #2ecc71;
+ }
+}
+
+@keyframes shake {
+ 0%, 100% { transform: translateX(0); }
+ 25% { transform: translateX(-10px); }
+ 75% { transform: translateX(10px); }
+}
+
+@keyframes fadeInUp {
+ from {
+ opacity: 0;
+ transform: translateY(30px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes fadeInDown {
+ from {
+ opacity: 0;
+ transform: translateY(-30px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+.reaction-message {
+ text-align: center;
+ padding: 2rem;
+ z-index: 2;
+}
+
+.message-text {
+ display: block;
+ font-size: 2.5rem;
+ color: white;
+ font-weight: 700;
+ text-shadow: 2px 2px 6px rgba(0, 0, 0, 0.4);
+ margin-bottom: 1rem;
+}
+
+.performance-badge {
+ margin-top: 1.5rem;
+ animation: bounceIn 0.6s ease-out;
+}
+
+.badge-emoji {
+ font-size: 3rem;
+ display: block;
+ margin-bottom: 0.5rem;
+}
+
+.badge-text {
+ font-size: 1.5rem;
+ font-weight: 600;
+ text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.3);
+}
+
+@keyframes bounceIn {
+ 0% {
+ transform: scale(0);
+ opacity: 0;
+ }
+ 50% {
+ transform: scale(1.1);
+ }
+ 100% {
+ transform: scale(1);
+ opacity: 1;
+ }
+}
+
+.too-early-message {
+ margin-top: 1rem;
+ animation: shake 0.5s ease-out;
+}
+
+.error-emoji {
+ font-size: 3rem;
+ display: block;
+ margin-bottom: 0.5rem;
+}
+
+.error-text {
+ font-size: 1.3rem;
+ color: white;
+ font-weight: 600;
+ text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.4);
+}
+
+.pulse-indicator {
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ bottom: 20%;
+}
+
+.pulse-ring {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ border: 3px solid rgba(255, 255, 255, 0.6);
+ border-radius: 50%;
+ animation: pulseRing 2s cubic-bezier(0.215, 0.61, 0.355, 1) infinite;
+}
+
+.pulse-ring:nth-child(2) {
+ animation-delay: 0.5s;
+}
+
+.pulse-ring:nth-child(3) {
+ animation-delay: 1s;
+}
+
+@keyframes pulseRing {
+ 0% {
+ transform: scale(0.5);
+ opacity: 1;
+ }
+ 100% {
+ transform: scale(1.5);
+ opacity: 0;
+ }
+}
+
+.reaction-instructions {
+ max-width: 600px;
+ margin: 0 auto 2rem auto;
+ text-align: center;
+ background: rgba(255, 255, 255, 0.1);
+ backdrop-filter: blur(10px);
+ padding: 1rem;
+ border-radius: 10px;
+ animation: fadeInUp 0.8s ease-out;
+}
+
+.reaction-instructions p {
+ color: white;
+ font-size: 1.1rem;
+ margin: 0;
+ font-weight: 500;
+}
+
+.reaction-stats {
+ max-width: 800px;
+ margin: 0 auto 2rem auto;
+ background: white;
+ border-radius: 20px;
+ padding: 2rem;
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
+ animation: fadeInUp 1s ease-out;
+}
+
+.stats-title {
+ font-size: 2rem;
+ color: #2c3e50;
+ margin: 0 0 1.5rem 0;
+ text-align: center;
+}
+
+.stats-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
+ gap: 1rem;
+ margin-bottom: 2rem;
+}
+
+.stat-card {
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ border-radius: 15px;
+ padding: 1.5rem;
+ text-align: center;
+ color: white;
+ transition: transform 0.3s ease;
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
+}
+
+.stat-card:hover {
+ transform: translateY(-5px);
+}
+
+.stat-card.highlight {
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
+ transform: scale(1.05);
+}
+
+.stat-icon {
+ font-size: 2.5rem;
+ margin-bottom: 0.5rem;
+}
+
+.stat-label {
+ font-size: 0.9rem;
+ opacity: 0.9;
+ margin-bottom: 0.3rem;
+ font-weight: 500;
+}
+
+.stat-value {
+ font-size: 1.8rem;
+ font-weight: 700;
+}
+
+.attempts-history {
+ margin-top: 2rem;
+}
+
+.history-title {
+ font-size: 1.5rem;
+ color: #2c3e50;
+ margin: 0 0 1rem 0;
+}
+
+.attempts-list {
+ max-height: 300px;
+ overflow-y: auto;
+ border: 2px solid #e0e0e0;
+ border-radius: 10px;
+ padding: 0.5rem;
+}
+
+.attempts-list::-webkit-scrollbar {
+ width: 8px;
+}
+
+.attempts-list::-webkit-scrollbar-track {
+ background: #f1f1f1;
+ border-radius: 10px;
+}
+
+.attempts-list::-webkit-scrollbar-thumb {
+ background: #667eea;
+ border-radius: 10px;
+}
+
+.attempts-list::-webkit-scrollbar-thumb:hover {
+ background: #764ba2;
+}
+
+.attempt-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0.8rem;
+ background: #f8f9fa;
+ border-radius: 8px;
+ margin-bottom: 0.5rem;
+ transition: all 0.3s ease;
+}
+
+.attempt-item:hover {
+ background: #e9ecef;
+ transform: translateX(5px);
+}
+
+.attempt-number {
+ font-weight: 600;
+ color: #667eea;
+ min-width: 40px;
+}
+
+.attempt-time {
+ font-weight: 700;
+ font-size: 1.1rem;
+ color: #2c3e50;
+ flex: 1;
+ text-align: center;
+}
+
+.attempt-rating {
+ font-weight: 600;
+ min-width: 150px;
+ text-align: right;
+}
+
+.reset-button {
+ width: 100%;
+ padding: 1rem;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ color: white;
+ border: none;
+ border-radius: 10px;
+ font-size: 1.1rem;
+ font-weight: 600;
+ cursor: pointer;
+ margin-top: 1.5rem;
+ transition: all 0.3s ease;
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
+}
+
+.reset-button:hover {
+ transform: translateY(-3px);
+ box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
+}
+
+.reset-button:active {
+ transform: translateY(-1px);
+}
+
+.reaction-info {
+ max-width: 800px;
+ margin: 0 auto;
+ background: rgba(255, 255, 255, 0.95);
+ border-radius: 20px;
+ padding: 2rem;
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
+ animation: fadeInUp 1.2s ease-out;
+}
+
+.info-title {
+ font-size: 1.8rem;
+ color: #2c3e50;
+ margin: 0 0 1rem 0;
+}
+
+.info-list {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+.info-list li {
+ padding: 0.8rem;
+ margin-bottom: 0.5rem;
+ background: #f8f9fa;
+ border-radius: 8px;
+ color: #495057;
+ font-size: 1rem;
+ border-left: 4px solid #667eea;
+ transition: all 0.3s ease;
+}
+
+.info-list li:hover {
+ background: #e9ecef;
+ transform: translateX(5px);
+}
+
+.info-list li strong {
+ color: #2ecc71;
+ font-weight: 700;
+}
+
+/* Responsive Design */
+@media (max-width: 768px) {
+ .reaction-time-container {
+ padding: 1rem;
+ }
+
+ .reaction-title {
+ font-size: 2rem;
+ }
+
+ .reaction-subtitle {
+ font-size: 1rem;
+ }
+
+ .reaction-game-area {
+ min-height: 300px;
+ }
+
+ .message-text {
+ font-size: 1.8rem;
+ }
+
+ .badge-emoji {
+ font-size: 2rem;
+ }
+
+ .badge-text {
+ font-size: 1.2rem;
+ }
+
+ .stats-grid {
+ grid-template-columns: repeat(2, 1fr);
+ }
+
+ .stat-value {
+ font-size: 1.5rem;
+ }
+
+ .reaction-stats,
+ .reaction-info {
+ padding: 1.5rem;
+ }
+
+ .attempt-rating {
+ min-width: 120px;
+ font-size: 0.9rem;
+ }
+}
+
+@media (max-width: 480px) {
+ .reaction-title {
+ font-size: 1.5rem;
+ }
+
+ .reaction-game-area {
+ min-height: 250px;
+ }
+
+ .message-text {
+ font-size: 1.5rem;
+ }
+
+ .stats-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .stat-card.highlight {
+ transform: scale(1);
+ }
+
+ .attempt-item {
+ flex-direction: column;
+ text-align: center;
+ gap: 0.5rem;
+ }
+
+ .attempt-rating {
+ min-width: auto;
+ text-align: center;
+ }
+
+ .info-list li {
+ font-size: 0.9rem;
+ }
+}
+