anycoder-18ee4845 / index.html
Tim13ekd's picture
Upload folder using huggingface_hub
b94d41c verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nochmal - Reaction Trainer</title>
<style>
/* CSS Variables for consistent theming */
:root {
--bg-color: #0f172a;
--surface-color: #1e293b;
--primary-color: #3b82f6;
--accent-color: #8b5cf6;
--success-color: #10b981;
--error-color: #ef4444;
--text-main: #f8fafc;
--text-muted: #94a3b8;
--font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
--card-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.3), 0 8px 10px -6px rgba(0, 0, 0, 0.3);
--transition-speed: 0.3s;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: var(--font-family);
background-color: var(--bg-color);
background-image: radial-gradient(circle at 10% 20%, #1e293b 0%, #0f172a 90%);
color: var(--text-main);
min-height: 100vh;
display: flex;
flex-direction: column;
overflow-x: hidden;
}
/* Header Styling */
header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.5rem 2rem;
background: rgba(15, 23, 42, 0.8);
backdrop-filter: blur(10px);
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
position: sticky;
top: 0;
z-index: 100;
}
.logo {
font-size: 1.5rem;
font-weight: 800;
background: linear-gradient(to right, var(--primary-color), var(--accent-color));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
letter-spacing: -0.05em;
}
.anycoder-link {
font-size: 0.85rem;
color: var(--text-muted);
text-decoration: none;
padding: 0.5rem 1rem;
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 20px;
transition: all var(--transition-speed);
}
.anycoder-link:hover {
color: var(--text-main);
border-color: var(--primary-color);
background: rgba(59, 130, 246, 0.1);
}
/* Main Layout */
main {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 2rem;
gap: 2rem;
max-width: 1200px;
margin: 0 auto;
width: 100%;
}
/* Interactive Game Area */
.game-container {
width: 100%;
max-width: 600px;
aspect-ratio: 16/9;
background: var(--surface-color);
border-radius: 24px;
box-shadow: var(--card-shadow);
position: relative;
overflow: hidden;
cursor: pointer;
user-select: none;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
transition: transform 0.1s ease;
border: 1px solid rgba(255, 255, 255, 0.05);
}
.game-container:active {
transform: scale(0.98);
}
/* Dynamic background states for the game container */
.game-container.state-idle {
border: 2px solid var(--primary-color);
}
.game-container.state-waiting {
background: var(--error-color);
border: none;
}
.game-container.state-go {
background: var(--success-color);
border: none;
}
.game-container.state-result {
border: 2px solid var(--accent-color);
}
.icon-large {
font-size: 4rem;
margin-bottom: 1rem;
opacity: 0.8;
}
.message-title {
font-size: 2rem;
font-weight: 700;
margin-bottom: 0.5rem;
}
.message-subtitle {
font-size: 1rem;
color: rgba(255, 255, 255, 0.8);
}
/* Stats Dashboard */
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 1.5rem;
width: 100%;
max-width: 800px;
}
.stat-card {
background: rgba(30, 41, 59, 0.5);
backdrop-filter: blur(5px);
padding: 1.5rem;
border-radius: 16px;
border: 1px solid rgba(255, 255, 255, 0.05);
text-align: center;
transition: transform var(--transition-speed);
}
.stat-card:hover {
transform: translateY(-5px);
background: rgba(30, 41, 59, 0.8);
}
.stat-value {
font-size: 2rem;
font-weight: 700;
color: var(--primary-color);
display: block;
margin-bottom: 0.25rem;
}
.stat-label {
font-size: 0.875rem;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.05em;
}
/* Controls */
.controls {
display: flex;
gap: 1rem;
margin-top: 1rem;
}
.btn {
padding: 1rem 2.5rem;
font-size: 1.1rem;
font-weight: 600;
border: none;
border-radius: 12px;
cursor: pointer;
transition: all var(--transition-speed);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.btn-primary {
background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
color: white;
box-shadow: 0 4px 15px rgba(59, 130, 246, 0.4);
}
.btn-primary:hover {
box-shadow: 0 6px 20px rgba(59, 130, 246, 0.6);
transform: translateY(-2px);
}
.btn-primary:active {
transform: translateY(0);
}
.btn-secondary {
background: transparent;
border: 2px solid var(--text-muted);
color: var(--text-muted);
}
.btn-secondary:hover {
border-color: var(--text-main);
color: var(--text-main);
}
/* History List */
.history-container {
width: 100%;
max-width: 600px;
background: rgba(15, 23, 42, 0.5);
border-radius: 16px;
padding: 1.5rem;
border: 1px solid rgba(255, 255, 255, 0.05);
}
.history-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
padding-bottom: 0.5rem;
}
.history-list {
list-style: none;
max-height: 150px;
overflow-y: auto;
}
.history-item {
display: flex;
justify-content: space-between;
padding: 0.5rem 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
font-size: 0.9rem;
}
.history-item:last-child {
border-bottom: none;
}
.history-time {
font-weight: 600;
}
.history-rank {
color: var(--text-muted);
}
/* Footer */
footer {
text-align: center;
padding: 2rem;
color: var(--text-muted);
font-size: 0.9rem;
}
/* Utilities */
.hidden {
display: none !important;
}
.fade-in {
animation: fadeIn 0.3s ease-in;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
/* Mobile Adjustments */
@media (max-width: 600px) {
.game-container {
aspect-ratio: 1/1;
}
.message-title {
font-size: 1.5rem;
}
.stats-grid {
grid-template-columns: 1fr 1fr;
}
header {
padding: 1rem;
}
}
</style>
</head>
<body>
<header>
<div class="logo">NOCHMAL</div>
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">
Built with anycoder
</a>
</header>
<main>
<!-- The Interactive Game Stage -->
<section id="game-stage" class="game-container state-idle" role="button" tabindex="0" aria-label="Game Area">
<div id="stage-content" class="fade-in">
<div class="icon-large"></div>
<div class="message-title">Reaction Test</div>
<div class="message-subtitle">Click anywhere to start</div>
</div>
</section>
<!-- Main Action Button -->
<div class="controls">
<button id="reset-btn" class="btn btn-primary hidden">Nochmal</button>
</div>
<!-- Statistics Grid -->
<section class="stats-grid">
<div class="stat-card">
<span id="last-score" class="stat-value">--</span>
<span class="stat-label">Last Time (ms)</span>
</div>
<div class="stat-card">
<span id="best-score" class="stat-value">--</span>
<span class="stat-label">Best Time (ms)</span>
</div>
<div class="stat-card">
<span id="avg-score" class="stat-value">--</span>
<span class="stat-label">Average (ms)</span>
</div>
</section>
<!-- History Section -->
<section class="history-container">
<div class="history-header">
<h3>Recent Attempts</h3>
<button id="clear-history" class="btn-secondary" style="padding: 0.25rem 0.75rem; font-size: 0.8rem; border-radius: 6px;">Clear</button>
</div>
<ul id="history-list" class="history-list">
<li class="history-item" style="justify-content: center; color: var(--text-muted);">No attempts yet</li>
</ul>
</section>
</main>
<footer>
<p>Train your brain. Improve your focus. Try NOCHMAL.</p>
</footer>
<script>
/**
* NOCHMAL - Reaction Time Trainer Logic
* Handles game states: idle, waiting, ready (go), result
*/
// --- DOM Elements ---
const gameStage = document.getElementById('game-stage');
const stageContent = document.getElementById('stage-content');
const resetBtn = document.getElementById('reset-btn');
const lastScoreEl = document.getElementById('last-score');
const bestScoreEl = document.getElementById('best-score');
const avgScoreEl = document.getElementById('avg-score');
const historyList = document.getElementById('history-list');
const clearHistoryBtn = document.getElementById('clear-history');
// --- State Variables ---
let gameState = 'idle'; // idle, waiting, ready, result
let startTime = 0;
let timeoutId = null;
let scores = [];
let bestScore = Infinity;
// --- Event Listeners ---
// Main interaction area click
gameStage.addEventListener('mousedown', handleInteraction);
// Touch support for mobile
gameStage.addEventListener('touchstart', (e) => {
e.preventDefault(); // Prevent double firing on some devices
handleInteraction();
});
// Keyboard support (Space/Enter)
gameStage.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleInteraction();
}
});
// Reset button click
resetBtn.addEventListener('click', (e) => {
e.stopPropagation(); // Prevent bubbling to gameStage
resetGame();
});
// Clear history
clearHistoryBtn.addEventListener('click', () => {
scores = [];
bestScore = Infinity;
updateStatsUI();
renderHistory();
});
// --- Core Functions ---
function handleInteraction() {
switch (gameState) {
case 'idle':
case 'result':
prepareGame();
break;
case 'waiting':
triggerTooEarly();
break;
case 'ready':
triggerSuccess();
break;
}
}
function prepareGame() {
gameState = 'waiting';
setStageVisuals('waiting', '⏳', 'Wait for Green...', 'Do not click yet.');
resetBtn.classList.add('hidden');
// Random delay between 2 and 5 seconds
const delay = Math.floor(Math.random() * 3000) + 2000;
timeoutId = setTimeout(() => {
triggerGo();
}, delay);
}
function triggerGo() {
gameState = 'ready';
startTime = performance.now();
setStageVisuals('go', '⚡', 'CLICK NOW!', 'Fast!');
}
function triggerTooEarly() {
clearTimeout(timeoutId);
gameState = 'result';
setStageVisuals('result', '⚠️', 'Too Early!', 'Click to try NOCHMAL.');
resetBtn.classList.remove('hidden');
}
function triggerSuccess() {
const endTime = performance.now();
const reactionTime = Math.round(endTime - startTime);
gameState = 'result';
// Update Logic
scores.push(reactionTime);
if (reactionTime < bestScore) bestScore = reactionTime;
// Update UI
updateStatsUI();
renderHistory();
let message = reactionTime < 250 ? 'Godlike!' :
reactionTime < 350 ? 'Great Job!' :
reactionTime < 500 ? 'Good.' : 'Keep practicing.';
setStageVisuals('result', '⏱️', `${reactionTime} ms`, message);
resetBtn.classList.remove('hidden');
}
function resetGame() {
gameState = 'idle';
setStageVisuals('idle', '⚡', 'Reaction Test', 'Click anywhere to start');
resetBtn.classList.add('hidden');
}
// --- UI Helper Functions ---
function setStageVisuals(stateClass, icon, title, subtitle) {
// Remove all state classes
gameStage.classList.remove('state-idle', 'state-waiting', 'state-go', 'state-result');
// Add current state class
gameStage.classList.add(`state-${stateClass}`);
// Update content with a small fade animation
stageContent.classList.remove('fade-in');
void stageContent.offsetWidth; // Trigger reflow
stageContent.classList.add('fade-in');
stageContent.innerHTML = `
<div class="icon-large">${icon}</div>
<div class="message-title">${title}</div>
<div class="message-subtitle">${subtitle}</div>
`;
}
function updateStatsUI() {
if (scores.length === 0) {
lastScoreEl.textContent = '--';
bestScoreEl.textContent = '--';
avgScoreEl.textContent = '--';
return;
}
const last = scores[scores.length - 1];
const sum = scores.reduce((a, b) => a + b, 0);
const avg = Math.round(sum / scores.length);
lastScoreEl.textContent = last;
bestScoreEl.textContent = bestScore;
avgScoreEl.textContent = avg;
// Color code the last score
lastScoreEl.style.color = last < 300 ? 'var(--success-color)' :
last < 500 ? 'var(--primary-color)' : 'var(--text-main)';
}
function renderHistory() {
if (scores.length === 0) {
historyList.innerHTML = '<li class="history-item" style="justify-content: center; color: var(--text-muted);">No attempts yet</li>';
return;
}
// Show last 5 attempts reversed
const recentScores = scores.slice(-5).reverse();
historyList.innerHTML = recentScores.map((score, index) => {
// Determine rank styling
let rankColor = 'var(--text-muted)';
if (score < 250) rankColor = 'var(--success-color)';
else if (score < 350) rankColor = 'var(--primary-color)';
return `
<li class="history-item fade-in">
<span class="history-rank">Attempt #${scores.length - index}</span>
<span class="history-time" style="color: ${rankColor}">${score} ms</span>
</li>
`;
}).join('');
}
// Initialize UI
resetGame();
</script>
</body>
</html>