Changes to speakandspell.scroll.pub

root
root
2 days ago
updated index.scroll
index.scroll
Changed around line 1
- buildHtml
- baseUrl https://speakandspell.scroll.pub
- metaTags
- editButton /edit.html
- title Speak & Spell - The Retro Spelling Game
- style.css
-
- header
- class hero
- nav
- div Speak & Spell
- class logo
- div
- class nav-links
- a Play
- href #play
- a About
- href #about
- a Edit
- href edit.html
- div
- class hero-content
- h1 Speak & Spell
- p The retro-inspired spelling game that helps you master difficult words
- a Start Playing
- class primary-button
- href #play
- main
- section
- id play
- class game-section
- div
- class game-container
- div
- class speaker-grille
- div
- class led-display
- div
- class word-display
- span
- class current-word
- span
- class input-display
- div
- class controls
- button
- class control-button
- id play-button
- span Play
- button
- class control-button
- id skip-button
- span Skip
- button
- class control-button
- id hint-button
- span Hint
- button
- class control-button
- id enter-button
- span Enter
- div
- class keyboard
- div
- class keyboard-row
- button Q
- button W
- button E
- button R
- button T
- button Y
- button U
- button I
- button O
- button P
- div
- class keyboard-row
- button A
- button S
- button D
- button F
- button G
- button H
- button J
- button K
- button L
- div
- class keyboard-row
- button Z
- button X
- button C
- button V
- button B
- button N
- button M
- button
- class backspace-button
- span ←
- div
- class stats
- div
- class stat-item
- span Correct:
- span
- id correct-count
- div
- class stat-item
- span Attempted:
- span
- id attempted-count
- div
- class stat-item
- span Success Rate:
- span
- id success-rate
- div
- class stat-item
- span Skipped:
- span
- id skipped-count
- button
- class reset-button
- id reset-button
- span Reset Game
- section
- id about
- class about-section
- h2 About Speak & Spell
- p This retro-inspired spelling game helps you master difficult words through an interactive experience. Built with modern web technologies but designed to evoke nostalgia for the classic Speak & Spell educational toy.
- p Features:
- ul
- li
- span Retro-inspired design
- li
- span 75 challenging words
- li
- span Speech synthesis
- li
- span Interactive keyboard
- li
- span Progress tracking
- footer
- div
- class footer-content
- div
- class footer-links
- a Documentation
- href https://scroll.pub/tutorial.html
- a Community
- href https://www.reddit.com/r/WorldWideScroll/
- a Blog
- href https://scroll.pub/blog
- p Created with ❤️ by the Scroll community
- script.js
+ import React, { useState, useEffect, useCallback, useRef } from 'react';
+ import { Button } from '@/components/ui/button';
+ import { Card } from '@/components/ui/card';
+ import { Alert, AlertDescription } from '@/components/ui/alert';
+ import { RotateCw, Volume2, Forward, HelpCircle, RefreshCw } from 'lucide-react';
+
+ function SpeakAndSpell() {
+ const successSoundRef = useRef(null);
+ const errorSoundRef = useRef(null);
+
+ const DIFFICULT_WORDS = [
+ 'accommodate', 'accordance', 'acquisition', 'ambiguous', 'bureaucracy',
+ 'characteristic', 'circumstantial', 'coincidence', 'colleague', 'commemorate',
+ 'commissary', 'commitment', 'committee', 'competitive', 'completely',
+ 'concurrence', 'conflagration', 'conscientious', 'consistent', 'convenient',
+ 'correspondence', 'definitely', 'development', 'difference', 'discipline',
+ 'dissimilar', 'efficient', 'embarrass', 'especially', 'exhilarate',
+ 'experience', 'fascinating', 'government', 'grandiloquent', 'guarantee',
+ 'harassment', 'hierarchy', 'humorous', 'hypocrite', 'ignominious',
+ 'immediate', 'incandescent', 'incredible', 'independent', 'industrial',
+ 'infinitesimal', 'influential', 'inspiration', 'intelligence', 'maintenance',
+ 'medieval', 'millennium', 'miniature', 'necessary', 'obstinate',
+ 'occurrence', 'opportunity', 'parliament', 'perseverance', 'personnel',
+ 'perspective', 'phenomenon', 'preference', 'prejudice', 'prevalent'
+ ];
+
+ const KEYBOARD_LAYOUT = [
+ ['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'],
+ ['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L'],
+ ['Z', 'X', 'C', 'V', 'B', 'N', 'M']
+ ];
+
+ const [availableWords, setAvailableWords] = useState([...DIFFICULT_WORDS]);
+ const [currentWord, setCurrentWord] = useState('');
+ const [userInput, setUserInput] = useState('');
+ const [stats, setStats] = useState({ correct: 0, attempted: 0, skipped: 0 });
+ const [showSuccess, setShowSuccess] = useState(false);
+ const [showError, setShowError] = useState(false);
+ const [showHint, setShowHint] = useState(false);
+ const [isLoading, setIsLoading] = useState(false);
+
+ // Create beep oscillator for success/error sounds
+ const playSuccessSound = () => {
+ if (typeof window !== 'undefined' && window.AudioContext) {
+ const audioContext = new AudioContext();
+ const oscillator = audioContext.createOscillator();
+ const gainNode = audioContext.createGain();
+
+ oscillator.connect(gainNode);
+ gainNode.connect(audioContext.destination);
+
+ oscillator.type = 'sine';
+ oscillator.frequency.setValueAtTime(800, audioContext.currentTime); // Higher pitch for success
+ gainNode.gain.setValueAtTime(0.5, audioContext.currentTime);
+
+ oscillator.start();
+ gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5);
+ oscillator.stop(audioContext.currentTime + 0.5);
+ }
+ };
+
+ const playErrorSound = () => {
+ if (typeof window !== 'undefined' && window.AudioContext) {
+ const audioContext = new AudioContext();
+ const oscillator = audioContext.createOscillator();
+ const gainNode = audioContext.createGain();
+
+ oscillator.connect(gainNode);
+ gainNode.connect(audioContext.destination);
+
+ oscillator.type = 'sine';
+ oscillator.frequency.setValueAtTime(200, audioContext.currentTime); // Lower pitch for error
+ gainNode.gain.setValueAtTime(0.5, audioContext.currentTime);
+
+ oscillator.start();
+ gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5);
+ oscillator.stop(audioContext.currentTime + 0.5);
+ }
+ };
+
+ useEffect(() => {
+ if (!currentWord) {
+ selectNewWord();
+ }
+ }, [currentWord]);
+
+ const selectNewWord = useCallback(() => {
+ if (availableWords.length === 0) {
+ setAvailableWords([...DIFFICULT_WORDS]);
+ return;
+ }
+ const index = Math.floor(Math.random() * availableWords.length);
+ const word = availableWords[index];
+ setAvailableWords(prev => prev.filter((_, i) => i !== index));
+ setCurrentWord(word);
+ }, [availableWords]);
+
+ const speakWord = (word) => {
+ setIsLoading(true);
+ const utterance = new SpeechSynthesisUtterance(word);
+ const voices = window.speechSynthesis.getVoices();
+ const maleVoice = voices.find(
+ voice =>
+ voice.name.includes('Male') ||
+ voice.name.includes('male') ||
+ voice.name.includes('David') ||
+ voice.name.includes('James') ||
+ voice.name.includes('Daniel')
+ );
+ if (maleVoice) {
+ utterance.voice = maleVoice;
+ }
+ utterance.rate = 0.9;
+ utterance.pitch = 0.8;
+ utterance.volume = 1.0;
+ utterance.onend = () => setIsLoading(false);
+ window.speechSynthesis.speak(utterance);
+ };
+
+ const speakLetter = (letter) => {
+ const utterance = new SpeechSynthesisUtterance(letter.toLowerCase());
+ const voices = window.speechSynthesis.getVoices();
+ const maleVoice = voices.find(
+ voice =>
+ voice.name.includes('Male') ||
+ voice.name.includes('male') ||
+ voice.name.includes('David') ||
+ voice.name.includes('James') ||
+ voice.name.includes('Daniel')
+ );
+ if (maleVoice) {
+ utterance.voice = maleVoice;
+ }
+ utterance.rate = 0.9;
+ utterance.pitch = 0.8;
+ utterance.volume = 1.0;
+ window.speechSynthesis.speak(utterance);
+ };
+
+ const handleKeyPress = (letter) => {
+ if (userInput.length < currentWord.length) {
+ setUserInput(prev => prev + letter);
+ speakLetter(letter);
+ }
+ };
+
+ const handleBackspace = () => {
+ setUserInput(prev => prev.slice(0, -1));
+ };
+
+ useEffect(() => {
+ const handlePhysicalKeyboard = (e) => {
+ if (e.key === 'Backspace') {
+ e.preventDefault();
+ handleBackspace();
+ } else if (e.key === 'Enter') {
+ e.preventDefault();
+ handleSubmit();
+ } else if (e.key.length === 1 && e.key.match(/[a-zA-Z]/i)) {
+ e.preventDefault();
+ handleKeyPress(e.key.toUpperCase());
+ }
+ };
+
+ window.addEventListener('keydown', handlePhysicalKeyboard);
+ return () => window.removeEventListener('keydown', handlePhysicalKeyboard);
+ }, [userInput, currentWord]);
+
+ const handleSkip = () => {
+ setStats(prev => ({ ...prev, skipped: prev.skipped + 1 }));
+ setUserInput('');
+ setShowHint(false);
+ selectNewWord();
+ };
+
+ const handleSubmit = () => {
+ if (userInput.toLowerCase() === currentWord.toLowerCase()) {
+ setShowSuccess(true);
+ setShowError(false);
+ setStats(prev => ({
+ ...prev,
+ correct: prev.correct + 1,
+ attempted: prev.attempted + 1
+ }));
+ playSuccessSound();
+ setTimeout(() => {
+ setShowSuccess(false);
+ setUserInput('');
+ setShowHint(false);
+ selectNewWord();
+ }, 1500);
+ } else {
+ setShowError(true);
+ setShowSuccess(false);
+ setStats(prev => ({
+ ...prev,
+ attempted: prev.attempted + 1
+ }));
+ playErrorSound();
+ setTimeout(() => {
+ setShowError(false);
+ setUserInput('');
+ }, 1500);
+ }
+ };
+
+ const handleHint = () => {
+ setShowHint(!showHint);
+ };
+
+ const handleReset = () => {
+ setAvailableWords([...DIFFICULT_WORDS]);
+ setCurrentWord('');
+ setUserInput('');
+ setStats({ correct: 0, attempted: 0, skipped: 0 });
+ setShowSuccess(false);
+ setShowError(false);
+ setShowHint(false);
+ };
+
+ return (
+
+
+ {/* Handle section */}
+
+
+ {/* Main device body */}
+
+ {/* Speaker grille */}
+
+ {[...Array(24)].map((_, i) => (
+
+ ))}
+
+
+ {/* Display section with retro LED look */}
+
+ {/* Success/Error Messages */}
+ {showSuccess && (
+
+ Correct!
+
+ )}
+ {showError && (
+
+ Incorrect, try again!
+
+ )}
+
+
+ style={{ color: '#32CD32', textShadow: '0 0 5px #32CD32' }}>
+ {userInput.split('').map((letter, index) => (
+
+ key={index}
+ className={
+ showHint && currentWord[index]?.toLowerCase() !== letter.toLowerCase()
+ ? 'text-red-500'
+ : ''
+ }
+ >
+ {letter}
+
+ ))}
+ {userInput.length < currentWord.length &&
+ {'_'.repeat(currentWord.length - userInput.length)}
+ }
+
+
+
+ {/* Control panel with yellow inset */}
+
+ {/* Control buttons */}
+
+
+ onClick={() => speakWord(currentWord)}
+ className="bg-blue-600 hover:bg-blue-700 text-white rounded-lg shadow-md border-2 border-blue-700"
+ disabled={isLoading}
+ >
+ {isLoading ? 'Speaking...' : 'Play'}
+
+
+ onClick={handleSkip}
+ className="bg-blue-600 hover:bg-blue-700 text-white rounded-lg shadow-md border-2 border-blue-700"
+ >
+ Skip
+
+
+ onClick={handleHint}
+ className="bg-blue-600 hover:bg-blue-700 text-white rounded-lg shadow-md border-2 border-blue-700"
+ >
+ Hint
+
+
+ onClick={handleSubmit}
+ className="bg-blue-600 hover:bg-blue-700 text-white rounded-lg shadow-md border-2 border-blue-700"
+ >
+ Enter
+
+
+
+ {/* Keyboard section */}
+
+ {KEYBOARD_LAYOUT.map((row, rowIndex) => (
+
+ {row.map((letter) => (
+
+ key={letter}
+ onClick={() => handleKeyPress(letter)}
+ className="w-10 h-10 bg-yellow-400 hover:bg-yellow-500 text-gray-900 font-bold text-lg rounded-lg shadow-md border-2 border-yellow-500"
+ >
+ {letter}
+
+ ))}
+
+ ))}
+
+
+ onClick={handleBackspace}
+ className="px-6 py-2 bg-yellow-400 hover:bg-yellow-500 text-gray-900 font-bold rounded-lg shadow-md border-2 border-yellow-500"
+ >
+ ←
+
+
+
+
+ {/* Logo at bottom */}
+
+
+
+
+
+
+
+
+ style={{
+ fontFamily: 'Arial',
+ fontSize: '48px',
+ fontWeight: 'bold'
+ }}>
+ Sp
+ ea
+ k
+ &
+ Sp
+ e
+ ll
+
+
+
+
+
+ {/* Stats display */}
+
+
Correct: {stats.correct} / Total: {stats.attempted + stats.skipped}
+
Success: {((stats.correct / (stats.attempted || 1)) * 100).toFixed(1)}%
+
Skipped: {stats.skipped}
+
+
+ {/* Reset button */}
+
+
+ onClick={handleReset}
+ className="bg-red-700 hover:bg-red-800 text-white rounded-lg shadow-md border-2 border-red-800"
+ >
+ Reset
+
+
+
+
+
+ );
+ }
+
+ export default SpeakAndSpell;
root
root
2 days ago
Initial commit
index.scroll
Changed around line 1
+ buildHtml
+ baseUrl https://speakandspell.scroll.pub
+ metaTags
+ editButton /edit.html
+ title Speak & Spell - The Retro Spelling Game
+ style.css
+
+ header
+ class hero
+ nav
+ div Speak & Spell
+ class logo
+ div
+ class nav-links
+ a Play
+ href #play
+ a About
+ href #about
+ a Edit
+ href edit.html
+ div
+ class hero-content
+ h1 Speak & Spell
+ p The retro-inspired spelling game that helps you master difficult words
+ a Start Playing
+ class primary-button
+ href #play
+ main
+ section
+ id play
+ class game-section
+ div
+ class game-container
+ div
+ class speaker-grille
+ div
+ class led-display
+ div
+ class word-display
+ span
+ class current-word
+ span
+ class input-display
+ div
+ class controls
+ button
+ class control-button
+ id play-button
+ span Play
+ button
+ class control-button
+ id skip-button
+ span Skip
+ button
+ class control-button
+ id hint-button
+ span Hint
+ button
+ class control-button
+ id enter-button
+ span Enter
+ div
+ class keyboard
+ div
+ class keyboard-row
+ button Q
+ button W
+ button E
+ button R
+ button T
+ button Y
+ button U
+ button I
+ button O
+ button P
+ div
+ class keyboard-row
+ button A
+ button S
+ button D
+ button F
+ button G
+ button H
+ button J
+ button K
+ button L
+ div
+ class keyboard-row
+ button Z
+ button X
+ button C
+ button V
+ button B
+ button N
+ button M
+ button
+ class backspace-button
+ span ←
+ div
+ class stats
+ div
+ class stat-item
+ span Correct:
+ span
+ id correct-count
+ div
+ class stat-item
+ span Attempted:
+ span
+ id attempted-count
+ div
+ class stat-item
+ span Success Rate:
+ span
+ id success-rate
+ div
+ class stat-item
+ span Skipped:
+ span
+ id skipped-count
+ button
+ class reset-button
+ id reset-button
+ span Reset Game
+ section
+ id about
+ class about-section
+ h2 About Speak & Spell
+ p This retro-inspired spelling game helps you master difficult words through an interactive experience. Built with modern web technologies but designed to evoke nostalgia for the classic Speak & Spell educational toy.
+ p Features:
+ ul
+ li
+ span Retro-inspired design
+ li
+ span 75 challenging words
+ li
+ span Speech synthesis
+ li
+ span Interactive keyboard
+ li
+ span Progress tracking
+ footer
+ div
+ class footer-content
+ div
+ class footer-links
+ a Documentation
+ href https://scroll.pub/tutorial.html
+ a Community
+ href https://www.reddit.com/r/WorldWideScroll/
+ a Blog
+ href https://scroll.pub/blog
+ p Created with ❤️ by the Scroll community
+ script.js
prompt.json
Changed around line 1
+ {
+ "id": "04ea995d-cd53-4751-a5ca-2f74584b5038",
+ "object": "chat.completion",
+ "created": 1738025838,
+ "model": "deepseek-chat",
+ "choices": [
+ {
+ "index": 0,
+ "message": {
+ "role": "assistant",
+ "content": "---domain---\nspeakandspell.scroll.pub\n---index.scroll---\nbuildHtml\nbaseUrl https://speakandspell.scroll.pub\nmetaTags\neditButton /edit.html\ntitle Speak & Spell - The Retro Spelling Game\nstyle.css\n\nheader\n class hero\n nav\n div Speak & Spell\n class logo\n div\n class nav-links\n a Play\n href #play\n a About\n href #about\n a Edit\n href edit.html\n div\n class hero-content\n h1 Speak & Spell\n p The retro-inspired spelling game that helps you master difficult words\n a Start Playing\n class primary-button\n href #play\nmain\n section\n id play\n class game-section\n div\n class game-container\n div\n class speaker-grille\n div\n class led-display\n div\n class word-display\n span\n class current-word\n span\n class input-display\n div\n class controls\n button\n class control-button\n id play-button\n span Play\n button\n class control-button\n id skip-button\n span Skip\n button\n class control-button\n id hint-button\n span Hint\n button\n class control-button\n id enter-button\n span Enter\n div\n class keyboard\n div\n class keyboard-row\n button Q\n button W\n button E\n button R\n button T\n button Y\n button U\n button I\n button O\n button P\n div\n class keyboard-row\n button A\n button S\n button D\n button F\n button G\n button H\n button J\n button K\n button L\n div\n class keyboard-row\n button Z\n button X\n button C\n button V\n button B\n button N\n button M\n button\n class backspace-button\n span ←\n div\n class stats\n div\n class stat-item\n span Correct:\n span\n id correct-count\n div\n class stat-item\n span Attempted:\n span\n id attempted-count\n div\n class stat-item\n span Success Rate:\n span\n id success-rate\n div\n class stat-item\n span Skipped:\n span\n id skipped-count\n button\n class reset-button\n id reset-button\n span Reset Game\n section\n id about\n class about-section\n h2 About Speak & Spell\n p This retro-inspired spelling game helps you master difficult words through an interactive experience. Built with modern web technologies but designed to evoke nostalgia for the classic Speak & Spell educational toy.\n p Features:\n ul\n li\n span Retro-inspired design\n li\n span 75 challenging words\n li\n span Speech synthesis\n li\n span Interactive keyboard\n li\n span Progress tracking\nfooter\n div\n class footer-content\n div\n class footer-links\n a Documentation\n href https://scroll.pub/tutorial.html\n a Community\n href https://www.reddit.com/r/WorldWideScroll/\n a Blog\n href https://scroll.pub/blog\n p Created with ❤️ by the Scroll community\nscript.js\n---style.css---\n:root {\n --primary: #ff4c4c;\n --secondary: #4c9eff;\n --accent: #ffd700;\n --bg: #f5f5f5;\n --text: #333;\n --retro-red: #ff0000;\n --retro-blue: #0000ff;\n --retro-yellow: #ffff00;\n --retro-green: #00ff00;\n}\n\nbody {\n font-family: 'Courier New', monospace;\n background: var(--bg);\n color: var(--text);\n margin: 0;\n padding: 0;\n}\n\n.hero {\n background: linear-gradient(135deg, var(--primary), var(--secondary));\n color: white;\n padding: 2rem;\n text-align: center;\n}\n\n.game-section {\n max-width: 800px;\n margin: 2rem auto;\n padding: 1rem;\n}\n\n.game-container {\n background: white;\n border: 8px solid var(--retro-red);\n border-radius: 20px;\n padding: 2rem;\n box-shadow: 0 10px 20px rgba(0,0,0,0.1);\n}\n\n.speaker-grille {\n display: grid;\n grid-template-columns: repeat(8, 1fr);\n gap: 2px;\n width: 150px;\n margin: 0 auto 1rem;\n}\n\n.speaker-grille div {\n background: #333;\n height: 4px;\n border-radius: 2px;\n}\n\n.led-display {\n background: black;\n padding: 1rem;\n border-radius: 10px;\n margin: 1rem 0;\n}\n\n.word-display {\n font-family: 'Courier New', monospace;\n font-size: 2rem;\n color: #32cd32;\n text-shadow: 0 0 5px #32cd32;\n text-align: center;\n}\n\n.controls {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 1rem;\n margin: 1rem 0;\n}\n\n.control-button {\n background: var(--retro-blue);\n color: white;\n border: none;\n padding: 0.5rem;\n border-radius: 5px;\n cursor: pointer;\n transition: all 0.2s;\n}\n\n.control-button:hover {\n background: var(--secondary);\n transform: scale(1.05);\n}\n\n.keyboard {\n background: var(--retro-yellow);\n padding: 1rem;\n border-radius: 10px;\n margin: 1rem 0;\n}\n\n.keyboard-row {\n display: flex;\n justify-content: center;\n gap: 0.5rem;\n margin: 0.5rem 0;\n}\n\n.keyboard button {\n background: white;\n border: 2px solid var(--retro-blue);\n padding: 1rem;\n border-radius: 5px;\n cursor: pointer;\n transition: all 0.2s;\n}\n\n.keyboard button:hover {\n background: var(--retro-green);\n transform: scale(1.1);\n}\n\n.stats {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 1rem;\n margin: 1rem 0;\n background: #333;\n color: white;\n padding: 1rem;\n border-radius: 10px;\n}\n\n.reset-button {\n background: var(--retro-red);\n color: white;\n border: none;\n padding: 0.5rem 1rem;\n border-radius: 5px;\n cursor: pointer;\n transition: all 0.2s;\n}\n\n.reset-button:hover {\n background: var(--primary);\n transform: scale(1.05);\n}\n\n@media (max-width: 768px) {\n .controls {\n grid-template-columns: repeat(2, 1fr);\n }\n \n .stats {\n grid-template-columns: repeat(2, 1fr);\n }\n \n .keyboard button {\n padding: 0.75rem;\n }\n}\n---script.js---\nconst DIFFICULT_WORDS = [\n 'accommodate', 'accordance', 'acquisition', 'ambiguous', 'bureaucracy',\n 'characteristic', 'circumstantial', 'coincidence', 'colleague', 'commemorate',\n 'commissary', 'commitment', 'committee', 'competitive', 'completely',\n 'concurrence', 'conflagration', 'conscientious', 'consistent', 'convenient',\n 'correspondence', 'definitely', 'development', 'difference', 'discipline',\n 'dissimilar', 'efficient', 'embarrass', 'especially', 'exhilarate',\n 'experience', 'fascinating', 'government', 'grandiloquent', 'guarantee',\n 'harassment', 'hierarchy', 'humorous', 'hypocrite', 'ignominious',\n 'immediate', 'incandescent', 'incredible', 'independent', 'industrial',\n 'infinitesimal', 'influential', 'inspiration', 'intelligence', 'maintenance',\n 'medieval', 'millennium', 'miniature', 'necessary', 'obstinate',\n 'occurrence', 'opportunity', 'parliament', 'perseverance', 'personnel',\n 'perspective', 'phenomenon', 'preference', 'prejudice', 'prevalent'\n];\n\nlet availableWords = [...DIFFICULT_WORDS];\nlet currentWord = '';\nlet userInput = '';\nlet stats = { correct: 0, attempted: 0, skipped: 0 };\n\nconst wordDisplay = document.querySelector('.current-word');\nconst inputDisplay = document.querySelector('.input-display');\nconst correctCount = document.getElementById('correct-count');\nconst attemptedCount = document.getElementById('attempted-count');\nconst successRate = document.getElementById('success-rate');\nconst skippedCount = document.getElementById('skipped-count');\n\nfunction selectNewWord() {\n if (availableWords.length === 0) {\n availableWords = [...DIFFICULT_WORDS];\n }\n const index = Math.floor(Math.random() * availableWords.length);\n currentWord = availableWords[index];\n availableWords.splice(index, 1);\n wordDisplay.textContent = currentWord;\n inputDisplay.textContent = '';\n userInput = '';\n}\n\nfunction speakWord(word) {\n const utterance = new SpeechSynthesisUtterance(word);\n const voices = window.speechSynthesis.getVoices();\n const maleVoice = voices.find(voice => \n voice.name.includes('Male') || \n voice.name.includes('male') || \n voice.name.includes('David') ||\n voice.name.includes('James') ||\n voice.name.includes('Daniel')\n );\n if (maleVoice) {\n utterance.voice = maleVoice;\n }\n utterance.rate = 0.9;\n utterance.pitch = 0.8;\n utterance.volume = 1.0;\n window.speechSynthesis.speak(utterance);\n}\n\nfunction updateStats() {\n correctCount.textContent = stats.correct;\n attemptedCount.textContent = stats.attempted;\n skippedCount.textContent = stats.skipped;\n successRate.textContent = `${((stats.correct / (stats.attempted || 1)) * 100).toFixed(1)}%`;\n}\n\ndocument.getElementById('play-button').addEventListener('click', () => {\n speakWord(currentWord);\n});\n\ndocument.getElementById('skip-button').addEventListener('click', () => {\n stats.skipped++;\n selectNewWord();\n updateStats();\n});\n\ndocument.getElementById('hint-button').addEventListener('click', () => {\n inputDisplay.textContent = currentWord;\n});\n\ndocument.getElementById('enter-button').addEventListener('click', () => {\n if (userInput.toLowerCase() === currentWord.toLowerCase()) {\n stats.correct++;\n stats.attempted++;\n alert('Correct!');\n selectNewWord();\n } else {\n stats.attempted++;\n alert('Incorrect, try again!');\n }\n updateStats();\n});\n\ndocument.querySelectorAll('.keyboard button').forEach(button => {\n if (!button.classList.contains('backspace-button')) {\n button.addEventListener('click', () => {\n if (userInput.length < currentWord.length) {\n userInput += button.textContent;\n inputDisplay.textContent = userInput;\n }\n });\n }\n});\n\ndocument.querySelector('.backspace-button').addEventListener('click', () => {\n userInput = userInput.slice(0, -1);\n inputDisplay.textContent = userInput;\n});\n\ndocument.getElementById('reset-button').addEventListener('click', () => {\n availableWords = [...DIFFICULT_WORDS];\n stats = { correct: 0, attempted: 0, skipped: 0 };\n selectNewWord();\n updateStats();\n});\n\n// Initialize game\nselectNewWord();\n---end---"
+ },
+ "logprobs": null,
+ "finish_reason": "stop"
+ }
+ ],
+ "usage": {
+ "prompt_tokens": 4216,
+ "completion_tokens": 2698,
+ "total_tokens": 6914,
+ "prompt_tokens_details": {
+ "cached_tokens": 0
+ },
+ "prompt_cache_hit_tokens": 0,
+ "prompt_cache_miss_tokens": 4216
+ },
+ "system_fingerprint": "fp_3a5770e1b4"
+ }
readme.scroll
Changed around line 1
+ # speakandspell.scroll.pub
+
+ Prompt: website
+ Agent: deepseek
+ Model: deepseek-chat
+
+ ## User prompt
+ import React, { useState, useEffect, useCallback, useRef } from 'react'; import { Button } from '@/components/ui/button'; import { Card } from '@/components/ui/card'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { RotateCw, Volume2, Forward, HelpCircle, RefreshCw } from 'lucide-react'; function SpeakAndSpell() { const successSoundRef = useRef(null); const errorSoundRef = useRef(null); const DIFFICULT_WORDS = [ 'accommodate', 'accordance', 'acquisition', 'ambiguous', 'bureaucracy', 'characteristic', 'circumstantial', 'coincidence', 'colleague', 'commemorate', 'commissary', 'commitment', 'committee', 'competitive', 'completely', 'concurrence', 'conflagration', 'conscientious', 'consistent', 'convenient', 'correspondence', 'definitely', 'development', 'difference', 'discipline', 'dissimilar', 'efficient', 'embarrass', 'especially', 'exhilarate', 'experience', 'fascinating', 'government', 'grandiloquent', 'guarantee', 'harassment', 'hierarchy', 'humorous', 'hypocrite', 'ignominious', 'immediate', 'incandescent', 'incredible', 'independent', 'industrial', 'infinitesimal', 'influential', 'inspiration', 'intelligence', 'maintenance', 'medieval', 'millennium', 'miniature', 'necessary', 'obstinate', 'occurrence', 'opportunity', 'parliament', 'perseverance', 'personnel', 'perspective', 'phenomenon', 'preference', 'prejudice', 'prevalent' ]; const KEYBOARD_LAYOUT = [ ['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'], ['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L'], ['Z', 'X', 'C', 'V', 'B', 'N', 'M'] ]; const [availableWords, setAvailableWords] = useState([...DIFFICULT_WORDS]); const [currentWord, setCurrentWord] = useState(''); const [userInput, setUserInput] = useState(''); const [stats, setStats] = useState({ correct: 0, attempted: 0, skipped: 0 }); const [showSuccess, setShowSuccess] = useState(false); const [showError, setShowError] = useState(false); const [showHint, setShowHint] = useState(false); const [isLoading, setIsLoading] = useState(false); // Create beep oscillator for success/error sounds const playSuccessSound = () => { if (typeof window !== 'undefined' && window.AudioContext) { const audioContext = new AudioContext(); const oscillator = audioContext.createOscillator(); const gainNode = audioContext.createGain(); oscillator.connect(gainNode); gainNode.connect(audioContext.destination); oscillator.type = 'sine'; oscillator.frequency.setValueAtTime(800, audioContext.currentTime); // Higher pitch for success gainNode.gain.setValueAtTime(0.5, audioContext.currentTime); oscillator.start(); gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5); oscillator.stop(audioContext.currentTime + 0.5); } }; const playErrorSound = () => { if (typeof window !== 'undefined' && window.AudioContext) { const audioContext = new AudioContext(); const oscillator = audioContext.createOscillator(); const gainNode = audioContext.createGain(); oscillator.connect(gainNode); gainNode.connect(audioContext.destination); oscillator.type = 'sine'; oscillator.frequency.setValueAtTime(200, audioContext.currentTime); // Lower pitch for error gainNode.gain.setValueAtTime(0.5, audioContext.currentTime); oscillator.start(); gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5); oscillator.stop(audioContext.currentTime + 0.5); } }; useEffect(() => { if (!currentWord) { selectNewWord(); } }, [currentWord]); const selectNewWord = useCallback(() => { if (availableWords.length === 0) { setAvailableWords([...DIFFICULT_WORDS]); return; } const index = Math.floor(Math.random() * availableWords.length); const word = availableWords[index]; setAvailableWords(prev => prev.filter((_, i) => i !== index)); setCurrentWord(word); }, [availableWords]); const speakWord = (word) => { setIsLoading(true); const utterance = new SpeechSynthesisUtterance(word); const voices = window.speechSynthesis.getVoices(); const maleVoice = voices.find( voice => voice.name.includes('Male') || voice.name.includes('male') || voice.name.includes('David') || voice.name.includes('James') || voice.name.includes('Daniel') ); if (maleVoice) { utterance.voice = maleVoice; } utterance.rate = 0.9; utterance.pitch = 0.8; utterance.volume = 1.0; utterance.onend = () => setIsLoading(false); window.speechSynthesis.speak(utterance); }; const speakLetter = (letter) => { const utterance = new SpeechSynthesisUtterance(letter.toLowerCase()); const voices = window.speechSynthesis.getVoices(); const maleVoice = voices.find( voice => voice.name.includes('Male') || voice.name.includes('male') || voice.name.includes('David') || voice.name.includes('James') || voice.name.includes('Daniel') ); if (maleVoice) { utterance.voice = maleVoice; } utterance.rate = 0.9; utterance.pitch = 0.8; utterance.volume = 1.0; window.speechSynthesis.speak(utterance); }; const handleKeyPress = (letter) => { if (userInput.length < currentWord.length) { setUserInput(prev => prev + letter); speakLetter(letter); } }; const handleBackspace = () => { setUserInput(prev => prev.slice(0, -1)); }; useEffect(() => { const handlePhysicalKeyboard = (e) => { if (e.key === 'Backspace') { e.preventDefault(); handleBackspace(); } else if (e.key === 'Enter') { e.preventDefault(); handleSubmit(); } else if (e.key.length === 1 && e.key.match(/[a-zA-Z]/i)) { e.preventDefault(); handleKeyPress(e.key.toUpperCase()); } }; window.addEventListener('keydown', handlePhysicalKeyboard); return () => window.removeEventListener('keydown', handlePhysicalKeyboard); }, [userInput, currentWord]); const handleSkip = () => { setStats(prev => ({ ...prev, skipped: prev.skipped + 1 })); setUserInput(''); setShowHint(false); selectNewWord(); }; const handleSubmit = () => { if (userInput.toLowerCase() === currentWord.toLowerCase()) { setShowSuccess(true); setShowError(false); setStats(prev => ({ ...prev, correct: prev.correct + 1, attempted: prev.attempted + 1 })); playSuccessSound(); setTimeout(() => { setShowSuccess(false); setUserInput(''); setShowHint(false); selectNewWord(); }, 1500); } else { setShowError(true); setShowSuccess(false); setStats(prev => ({ ...prev, attempted: prev.attempted + 1 })); playErrorSound(); setTimeout(() => { setShowError(false); setUserInput(''); }, 1500); } }; const handleHint = () => { setShowHint(!showHint); }; const handleReset = () => { setAvailableWords([...DIFFICULT_WORDS]); setCurrentWord(''); setUserInput(''); setStats({ correct: 0, attempted: 0, skipped: 0 }); setShowSuccess(false); setShowError(false); setShowHint(false); }; return (
{/* Handle section */}
{/* Main device body */}
{/* Speaker grille */}
{[...Array(24)].map((_, i) => (
))}
{/* Display section with retro LED look */}
{/* Success/Error Messages */} {showSuccess && ( Correct! )} {showError && ( Incorrect, try again! )}
{userInput.split('').map((letter, index) => ( {letter} ))} {userInput.length < currentWord.length && {'_'.repeat(currentWord.length - userInput.length)} }
{/* Control panel with yellow inset */}
{/* Control buttons */}
{/* Keyboard section */}
{KEYBOARD_LAYOUT.map((row, rowIndex) => (
{row.map((letter) => ( ))}
))}
{/* Logo at bottom */}
Sp ea k & Sp e ll
{/* Stats display */}
Correct: {stats.correct} / Total: {stats.attempted + stats.skipped}
Success: {((stats.correct / (stats.attempted || 1)) * 100).toFixed(1)}%
Skipped: {stats.skipped}
{/* Reset button */}
); } export default SpeakAndSpell;
+
+ ## System prompt
+ You are an expert web developer. Create a website based on this request: "import React, { useState, useEffect, useCallback, useRef } from 'react'; import { Button } from '@/components/ui/button'; import { Card } from '@/components/ui/card'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { RotateCw, Volume2, Forward, HelpCircle, RefreshCw } from 'lucide-react'; function SpeakAndSpell() { const successSoundRef = useRef(null); const errorSoundRef = useRef(null); const DIFFICULT_WORDS = [ 'accommodate', 'accordance', 'acquisition', 'ambiguous', 'bureaucracy', 'characteristic', 'circumstantial', 'coincidence', 'colleague', 'commemorate', 'commissary', 'commitment', 'committee', 'competitive', 'completely', 'concurrence', 'conflagration', 'conscientious', 'consistent', 'convenient', 'correspondence', 'definitely', 'development', 'difference', 'discipline', 'dissimilar', 'efficient', 'embarrass', 'especially', 'exhilarate', 'experience', 'fascinating', 'government', 'grandiloquent', 'guarantee', 'harassment', 'hierarchy', 'humorous', 'hypocrite', 'ignominious', 'immediate', 'incandescent', 'incredible', 'independent', 'industrial', 'infinitesimal', 'influential', 'inspiration', 'intelligence', 'maintenance', 'medieval', 'millennium', 'miniature', 'necessary', 'obstinate', 'occurrence', 'opportunity', 'parliament', 'perseverance', 'personnel', 'perspective', 'phenomenon', 'preference', 'prejudice', 'prevalent' ]; const KEYBOARD_LAYOUT = [ ['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'], ['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L'], ['Z', 'X', 'C', 'V', 'B', 'N', 'M'] ]; const [availableWords, setAvailableWords] = useState([...DIFFICULT_WORDS]); const [currentWord, setCurrentWord] = useState(''); const [userInput, setUserInput] = useState(''); const [stats, setStats] = useState({ correct: 0, attempted: 0, skipped: 0 }); const [showSuccess, setShowSuccess] = useState(false); const [showError, setShowError] = useState(false); const [showHint, setShowHint] = useState(false); const [isLoading, setIsLoading] = useState(false); // Create beep oscillator for success/error sounds const playSuccessSound = () => { if (typeof window !== 'undefined' && window.AudioContext) { const audioContext = new AudioContext(); const oscillator = audioContext.createOscillator(); const gainNode = audioContext.createGain(); oscillator.connect(gainNode); gainNode.connect(audioContext.destination); oscillator.type = 'sine'; oscillator.frequency.setValueAtTime(800, audioContext.currentTime); // Higher pitch for success gainNode.gain.setValueAtTime(0.5, audioContext.currentTime); oscillator.start(); gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5); oscillator.stop(audioContext.currentTime + 0.5); } }; const playErrorSound = () => { if (typeof window !== 'undefined' && window.AudioContext) { const audioContext = new AudioContext(); const oscillator = audioContext.createOscillator(); const gainNode = audioContext.createGain(); oscillator.connect(gainNode); gainNode.connect(audioContext.destination); oscillator.type = 'sine'; oscillator.frequency.setValueAtTime(200, audioContext.currentTime); // Lower pitch for error gainNode.gain.setValueAtTime(0.5, audioContext.currentTime); oscillator.start(); gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5); oscillator.stop(audioContext.currentTime + 0.5); } }; useEffect(() => { if (!currentWord) { selectNewWord(); } }, [currentWord]); const selectNewWord = useCallback(() => { if (availableWords.length === 0) { setAvailableWords([...DIFFICULT_WORDS]); return; } const index = Math.floor(Math.random() * availableWords.length); const word = availableWords[index]; setAvailableWords(prev => prev.filter((_, i) => i !== index)); setCurrentWord(word); }, [availableWords]); const speakWord = (word) => { setIsLoading(true); const utterance = new SpeechSynthesisUtterance(word); const voices = window.speechSynthesis.getVoices(); const maleVoice = voices.find( voice => voice.name.includes('Male') || voice.name.includes('male') || voice.name.includes('David') || voice.name.includes('James') || voice.name.includes('Daniel') ); if (maleVoice) { utterance.voice = maleVoice; } utterance.rate = 0.9; utterance.pitch = 0.8; utterance.volume = 1.0; utterance.onend = () => setIsLoading(false); window.speechSynthesis.speak(utterance); }; const speakLetter = (letter) => { const utterance = new SpeechSynthesisUtterance(letter.toLowerCase()); const voices = window.speechSynthesis.getVoices(); const maleVoice = voices.find( voice => voice.name.includes('Male') || voice.name.includes('male') || voice.name.includes('David') || voice.name.includes('James') || voice.name.includes('Daniel') ); if (maleVoice) { utterance.voice = maleVoice; } utterance.rate = 0.9; utterance.pitch = 0.8; utterance.volume = 1.0; window.speechSynthesis.speak(utterance); }; const handleKeyPress = (letter) => { if (userInput.length < currentWord.length) { setUserInput(prev => prev + letter); speakLetter(letter); } }; const handleBackspace = () => { setUserInput(prev => prev.slice(0, -1)); }; useEffect(() => { const handlePhysicalKeyboard = (e) => { if (e.key === 'Backspace') { e.preventDefault(); handleBackspace(); } else if (e.key === 'Enter') { e.preventDefault(); handleSubmit(); } else if (e.key.length === 1 && e.key.match(/[a-zA-Z]/i)) { e.preventDefault(); handleKeyPress(e.key.toUpperCase()); } }; window.addEventListener('keydown', handlePhysicalKeyboard); return () => window.removeEventListener('keydown', handlePhysicalKeyboard); }, [userInput, currentWord]); const handleSkip = () => { setStats(prev => ({ ...prev, skipped: prev.skipped + 1 })); setUserInput(''); setShowHint(false); selectNewWord(); }; const handleSubmit = () => { if (userInput.toLowerCase() === currentWord.toLowerCase()) { setShowSuccess(true); setShowError(false); setStats(prev => ({ ...prev, correct: prev.correct + 1, attempted: prev.attempted + 1 })); playSuccessSound(); setTimeout(() => { setShowSuccess(false); setUserInput(''); setShowHint(false); selectNewWord(); }, 1500); } else { setShowError(true); setShowSuccess(false); setStats(prev => ({ ...prev, attempted: prev.attempted + 1 })); playErrorSound(); setTimeout(() => { setShowError(false); setUserInput(''); }, 1500); } }; const handleHint = () => { setShowHint(!showHint); }; const handleReset = () => { setAvailableWords([...DIFFICULT_WORDS]); setCurrentWord(''); setUserInput(''); setStats({ correct: 0, attempted: 0, skipped: 0 }); setShowSuccess(false); setShowError(false); setShowHint(false); }; return (
{/* Handle section */}
{/* Main device body */}
{/* Speaker grille */}
{[...Array(24)].map((_, i) => (
))}
{/* Display section with retro LED look */}
{/* Success/Error Messages */} {showSuccess && ( Correct! )} {showError && ( Incorrect, try again! )}
{userInput.split('').map((letter, index) => ( {letter} ))} {userInput.length < currentWord.length && {'_'.repeat(currentWord.length - userInput.length)} }
{/* Control panel with yellow inset */}
{/* Control buttons */}
{/* Keyboard section */}
{KEYBOARD_LAYOUT.map((row, rowIndex) => (
{row.map((letter) => ( ))}
))}
{/* Logo at bottom */}
Sp ea k & Sp e ll
{/* Stats display */}
Correct: {stats.correct} / Total: {stats.attempted + stats.skipped}
Success: {((stats.correct / (stats.attempted || 1)) * 100).toFixed(1)}%
Skipped: {stats.skipped}
{/* Reset button */}
); } export default SpeakAndSpell;"
+
+ Requirements:
+ - Use only Scroll, CSS, and JavaScript (NO frameworks, NO external dependencies)
+ - Create clean, semantic HTML5
+ - Make it mobile-responsive
+ - Follow modern best practices and accessibility guidelines
+ - Keep it simple but professional
+ - Use only relative links and no external resources
+ - Do not put a copyright symbol or all rights reserved in the footer.
+ - Make it beautiful. Dazzling. Advanced used of CSS.
+
+ As a refresher, for doing the html body, Scroll is a whitespace based language that uses a single indented space to mark a line (aka particle) as a subparticle of a parent line.
+
+ For example:
+
+ header
+ class hero
+ nav
+ div Scroll
+ class logo
+ div
+ class nav-links
+ a Features
+ href #features
+ a Examples
+ href #examples
+ a Edit
+ href edit.html
+ a GitHub
+ class cta-button
+ href https://github.com/breck7/scroll
+ div
+ class hero-content
+ h1 Write Better with Scroll
+ p The extendible markup language that makes source beautiful and compiles to anything
+ a Get Started
+ class primary-button
+ href https://hub.scroll.pub/
+ main
+ section
+ id features
+ class features
+ h2 Why Scroll?
+ div
+ class feature-grid
+ div
+ class feature-card
+ div ⚡
+ class feature-icon
+ h3 Simple Syntax
+ p Like Markdown, but more powerful. No parentheses needed.
+ div
+ class feature-card
+ div 🧩
+ class feature-icon
+ h3 Extendible
+ p Build your own custom parsers.
+ div
+ class feature-card
+ div 🎨
+ class feature-icon
+ h3 Beautiful Output
+ p Create stunning documents with minimal effort.
+ div
+ class feature-card
+ div 🚀
+ class feature-icon
+ h3 Fast & Light
+ p Built on the efficient PPS Stack.
+ section
+ id examples
+ class code-demo
+ h2 See It In Action
+ div
+ class code-container
+ pre
+ class code-example
+ div
+ class code-output
+ footer
+ div
+ class footer-content
+ div
+ class footer-links
+ a Documentation
+ href https://scroll.pub/tutorial.html
+ a Community
+ href https://www.reddit.com/r/WorldWideScroll/
+ a Blog
+ href https://scroll.pub/blog
+ p Started by Breck Yunits. Evolved by a community.
+ https://twitter.com/breckyunits Breck Yunits
+ https://github.com/breck7/scroll/graphs/contributors community
+
+
+ First suggest a short, memorable domain name ending in scroll.pub that represents this website. Then provide the website files. Use this exact format:
+
+ ---domain---
+ (domainscroll.pub here)
+ ---index.scroll---
+ buildHtml
+ baseUrl https://(domainscroll.pub here)
+ metaTags
+ editButton /edit.html
+ title (Title here)
+ style.css
+
+ (body content here. no blank lines please.)
+
+ script.js
+ ---style.css---
+ (CSS content here)
+ ---script.js---
+ (JavaScript content here)
+ ---end---
script.js
Changed around line 1
+ const DIFFICULT_WORDS = [
+ 'accommodate', 'accordance', 'acquisition', 'ambiguous', 'bureaucracy',
+ 'characteristic', 'circumstantial', 'coincidence', 'colleague', 'commemorate',
+ 'commissary', 'commitment', 'committee', 'competitive', 'completely',
+ 'concurrence', 'conflagration', 'conscientious', 'consistent', 'convenient',
+ 'correspondence', 'definitely', 'development', 'difference', 'discipline',
+ 'dissimilar', 'efficient', 'embarrass', 'especially', 'exhilarate',
+ 'experience', 'fascinating', 'government', 'grandiloquent', 'guarantee',
+ 'harassment', 'hierarchy', 'humorous', 'hypocrite', 'ignominious',
+ 'immediate', 'incandescent', 'incredible', 'independent', 'industrial',
+ 'infinitesimal', 'influential', 'inspiration', 'intelligence', 'maintenance',
+ 'medieval', 'millennium', 'miniature', 'necessary', 'obstinate',
+ 'occurrence', 'opportunity', 'parliament', 'perseverance', 'personnel',
+ 'perspective', 'phenomenon', 'preference', 'prejudice', 'prevalent'
+ ];
+
+ let availableWords = [...DIFFICULT_WORDS];
+ let currentWord = '';
+ let userInput = '';
+ let stats = { correct: 0, attempted: 0, skipped: 0 };
+
+ const wordDisplay = document.querySelector('.current-word');
+ const inputDisplay = document.querySelector('.input-display');
+ const correctCount = document.getElementById('correct-count');
+ const attemptedCount = document.getElementById('attempted-count');
+ const successRate = document.getElementById('success-rate');
+ const skippedCount = document.getElementById('skipped-count');
+
+ function selectNewWord() {
+ if (availableWords.length === 0) {
+ availableWords = [...DIFFICULT_WORDS];
+ }
+ const index = Math.floor(Math.random() * availableWords.length);
+ currentWord = availableWords[index];
+ availableWords.splice(index, 1);
+ wordDisplay.textContent = currentWord;
+ inputDisplay.textContent = '';
+ userInput = '';
+ }
+
+ function speakWord(word) {
+ const utterance = new SpeechSynthesisUtterance(word);
+ const voices = window.speechSynthesis.getVoices();
+ const maleVoice = voices.find(voice =>
+ voice.name.includes('Male') ||
+ voice.name.includes('male') ||
+ voice.name.includes('David') ||
+ voice.name.includes('James') ||
+ voice.name.includes('Daniel')
+ );
+ if (maleVoice) {
+ utterance.voice = maleVoice;
+ }
+ utterance.rate = 0.9;
+ utterance.pitch = 0.8;
+ utterance.volume = 1.0;
+ window.speechSynthesis.speak(utterance);
+ }
+
+ function updateStats() {
+ correctCount.textContent = stats.correct;
+ attemptedCount.textContent = stats.attempted;
+ skippedCount.textContent = stats.skipped;
+ successRate.textContent = `${((stats.correct / (stats.attempted || 1)) * 100).toFixed(1)}%`;
+ }
+
+ document.getElementById('play-button').addEventListener('click', () => {
+ speakWord(currentWord);
+ });
+
+ document.getElementById('skip-button').addEventListener('click', () => {
+ stats.skipped++;
+ selectNewWord();
+ updateStats();
+ });
+
+ document.getElementById('hint-button').addEventListener('click', () => {
+ inputDisplay.textContent = currentWord;
+ });
+
+ document.getElementById('enter-button').addEventListener('click', () => {
+ if (userInput.toLowerCase() === currentWord.toLowerCase()) {
+ stats.correct++;
+ stats.attempted++;
+ alert('Correct!');
+ selectNewWord();
+ } else {
+ stats.attempted++;
+ alert('Incorrect, try again!');
+ }
+ updateStats();
+ });
+
+ document.querySelectorAll('.keyboard button').forEach(button => {
+ if (!button.classList.contains('backspace-button')) {
+ button.addEventListener('click', () => {
+ if (userInput.length < currentWord.length) {
+ userInput += button.textContent;
+ inputDisplay.textContent = userInput;
+ }
+ });
+ }
+ });
+
+ document.querySelector('.backspace-button').addEventListener('click', () => {
+ userInput = userInput.slice(0, -1);
+ inputDisplay.textContent = userInput;
+ });
+
+ document.getElementById('reset-button').addEventListener('click', () => {
+ availableWords = [...DIFFICULT_WORDS];
+ stats = { correct: 0, attempted: 0, skipped: 0 };
+ selectNewWord();
+ updateStats();
+ });
+
+ // Initialize game
+ selectNewWord();
style.css
Changed around line 1
+ :root {
+ --primary: #ff4c4c;
+ --secondary: #4c9eff;
+ --accent: #ffd700;
+ --bg: #f5f5f5;
+ --text: #333;
+ --retro-red: #ff0000;
+ --retro-blue: #0000ff;
+ --retro-yellow: #ffff00;
+ --retro-green: #00ff00;
+ }
+
+ body {
+ font-family: 'Courier New', monospace;
+ background: var(--bg);
+ color: var(--text);
+ margin: 0;
+ padding: 0;
+ }
+
+ .hero {
+ background: linear-gradient(135deg, var(--primary), var(--secondary));
+ color: white;
+ padding: 2rem;
+ text-align: center;
+ }
+
+ .game-section {
+ max-width: 800px;
+ margin: 2rem auto;
+ padding: 1rem;
+ }
+
+ .game-container {
+ background: white;
+ border: 8px solid var(--retro-red);
+ border-radius: 20px;
+ padding: 2rem;
+ box-shadow: 0 10px 20px rgba(0,0,0,0.1);
+ }
+
+ .speaker-grille {
+ display: grid;
+ grid-template-columns: repeat(8, 1fr);
+ gap: 2px;
+ width: 150px;
+ margin: 0 auto 1rem;
+ }
+
+ .speaker-grille div {
+ background: #333;
+ height: 4px;
+ border-radius: 2px;
+ }
+
+ .led-display {
+ background: black;
+ padding: 1rem;
+ border-radius: 10px;
+ margin: 1rem 0;
+ }
+
+ .word-display {
+ font-family: 'Courier New', monospace;
+ font-size: 2rem;
+ color: #32cd32;
+ text-shadow: 0 0 5px #32cd32;
+ text-align: center;
+ }
+
+ .controls {
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ gap: 1rem;
+ margin: 1rem 0;
+ }
+
+ .control-button {
+ background: var(--retro-blue);
+ color: white;
+ border: none;
+ padding: 0.5rem;
+ border-radius: 5px;
+ cursor: pointer;
+ transition: all 0.2s;
+ }
+
+ .control-button:hover {
+ background: var(--secondary);
+ transform: scale(1.05);
+ }
+
+ .keyboard {
+ background: var(--retro-yellow);
+ padding: 1rem;
+ border-radius: 10px;
+ margin: 1rem 0;
+ }
+
+ .keyboard-row {
+ display: flex;
+ justify-content: center;
+ gap: 0.5rem;
+ margin: 0.5rem 0;
+ }
+
+ .keyboard button {
+ background: white;
+ border: 2px solid var(--retro-blue);
+ padding: 1rem;
+ border-radius: 5px;
+ cursor: pointer;
+ transition: all 0.2s;
+ }
+
+ .keyboard button:hover {
+ background: var(--retro-green);
+ transform: scale(1.1);
+ }
+
+ .stats {
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ gap: 1rem;
+ margin: 1rem 0;
+ background: #333;
+ color: white;
+ padding: 1rem;
+ border-radius: 10px;
+ }
+
+ .reset-button {
+ background: var(--retro-red);
+ color: white;
+ border: none;
+ padding: 0.5rem 1rem;
+ border-radius: 5px;
+ cursor: pointer;
+ transition: all 0.2s;
+ }
+
+ .reset-button:hover {
+ background: var(--primary);
+ transform: scale(1.05);
+ }
+
+ @media (max-width: 768px) {
+ .controls {
+ grid-template-columns: repeat(2, 1fr);
+ }
+
+ .stats {
+ grid-template-columns: repeat(2, 1fr);
+ }
+
+ .keyboard button {
+ padding: 0.75rem;
+ }
+ }