Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Voice Assistant Demo</title> | |
| <style> | |
| body { | |
| font-family: system-ui, -apple-system, sans-serif; | |
| max-width: 800px; | |
| margin: 0 auto; | |
| padding: 20px; | |
| background: #f0f0f0; | |
| } | |
| .container { | |
| background: white; | |
| padding: 20px; | |
| border-radius: 8px; | |
| box-shadow: 0 2px 4px rgba(0,0,0,0.1); | |
| } | |
| .status { | |
| margin: 20px 0; | |
| padding: 10px; | |
| border-radius: 4px; | |
| background: #e8f5e9; | |
| } | |
| .transcript { | |
| margin: 20px 0; | |
| padding: 15px; | |
| background: #f5f5f5; | |
| border-radius: 4px; | |
| min-height: 50px; | |
| } | |
| button { | |
| background: #2196F3; | |
| color: white; | |
| border: none; | |
| padding: 10px 20px; | |
| border-radius: 4px; | |
| cursor: pointer; | |
| font-size: 16px; | |
| } | |
| button:hover { | |
| background: #1976D2; | |
| } | |
| button:disabled { | |
| background: #ccc; | |
| cursor: not-allowed; | |
| } | |
| .controls { | |
| display: flex; | |
| gap: 10px; | |
| margin: 20px 0; | |
| } | |
| .voice-settings { | |
| margin: 20px 0; | |
| padding: 15px; | |
| background: #f8f9fa; | |
| border-radius: 4px; | |
| } | |
| .setting-group { | |
| margin: 10px 0; | |
| } | |
| label { | |
| display: inline-block; | |
| width: 100px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1>Voice Assistant Demo</h1> | |
| <p>Click "Start Listening" and speak a command. Try saying:</p> | |
| <ul> | |
| <li>"Hello" - The assistant will greet you back</li> | |
| <li>"What time is it?" - Get the current time</li> | |
| <li>"Tell me a fact" - Hear an interesting fact</li> | |
| </ul> | |
| <div class="controls"> | |
| <button id="startBtn">Start Listening</button> | |
| <button id="stopBtn" disabled>Stop Listening</button> | |
| </div> | |
| <div class="status" id="status">Status: Ready</div> | |
| <div class="transcript" id="transcript"></div> | |
| <div class="voice-settings"> | |
| <h3>Voice Settings</h3> | |
| <div class="setting-group"> | |
| <label for="voice">Voice:</label> | |
| <select id="voice"></select> | |
| </div> | |
| <div class="setting-group"> | |
| <label for="rate">Rate:</label> | |
| <input type="range" id="rate" min="0.5" max="2" value="1" step="0.1"> | |
| <span id="rateValue">1</span> | |
| </div> | |
| <div class="setting-group"> | |
| <label for="pitch">Pitch:</label> | |
| <input type="range" id="pitch" min="0.5" max="2" value="1" step="0.1"> | |
| <span id="pitchValue">1</span> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Initialize speech recognition | |
| const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; | |
| const recognition = new SpeechRecognition(); | |
| recognition.continuous = false; | |
| recognition.lang = 'en-US'; | |
| recognition.interimResults = false; | |
| recognition.maxAlternatives = 1; | |
| // Initialize speech synthesis | |
| const synth = window.speechSynthesis; | |
| let voices = []; | |
| // DOM elements | |
| const startBtn = document.getElementById('startBtn'); | |
| const stopBtn = document.getElementById('stopBtn'); | |
| const status = document.getElementById('status'); | |
| const transcript = document.getElementById('transcript'); | |
| const voiceSelect = document.getElementById('voice'); | |
| const rate = document.getElementById('rate'); | |
| const pitch = document.getElementById('pitch'); | |
| const rateValue = document.getElementById('rateValue'); | |
| const pitchValue = document.getElementById('pitchValue'); | |
| // Populate voice list | |
| function populateVoices() { | |
| voices = synth.getVoices(); | |
| voiceSelect.innerHTML = ''; | |
| voices.forEach((voice, i) => { | |
| const option = document.createElement('option'); | |
| option.textContent = `${voice.name} (${voice.lang})`; | |
| option.setAttribute('data-name', voice.name); | |
| voiceSelect.appendChild(option); | |
| }); | |
| } | |
| populateVoices(); | |
| if (speechSynthesis.onvoiceschanged !== undefined) { | |
| speechSynthesis.onvoiceschanged = populateVoices; | |
| } | |
| // Speech synthesis function | |
| function speak(text) { | |
| if (synth.speaking) { | |
| synth.cancel(); | |
| } | |
| const utterance = new SpeechSynthesisUtterance(text); | |
| const selectedVoice = voices[voiceSelect.selectedIndex]; | |
| utterance.voice = selectedVoice; | |
| utterance.rate = rate.value; | |
| utterance.pitch = pitch.value; | |
| synth.speak(utterance); | |
| } | |
| // Handle commands | |
| function handleCommand(command) { | |
| command = command.toLowerCase(); | |
| let response = ''; | |
| if (command.includes('hello')) { | |
| response = 'Hello! How can I help you today?'; | |
| } else if (command.includes('what time')) { | |
| const now = new Date(); | |
| response = `The current time is ${now.toLocaleTimeString()}`; | |
| } else if (command.includes('tell me a fact')) { | |
| const facts = [ | |
| 'The shortest war in history was between Britain and Zanzibar on August 27, 1896. Zanzibar surrendered after just 38 minutes.', | |
| 'Honey never spoils. Archaeologists have found pots of honey in ancient Egyptian tombs that are over 3,000 years old and still perfectly good to eat.', | |
| 'The first oranges weren't orange. The original oranges from Southeast Asia were actually green.', | |
| ]; | |
| response = facts[Math.floor(Math.random() * facts.length)]; | |
| } else { | |
| response = "I'm sorry, I didn't understand that command. Please try again."; | |
| } | |
| transcript.textContent = `You said: ${command}\nAssistant: ${response}`; | |
| speak(response); | |
| } | |
| // Event listeners | |
| startBtn.addEventListener('click', () => { | |
| recognition.start(); | |
| startBtn.disabled = true; | |
| stopBtn.disabled = false; | |
| status.textContent = 'Status: Listening...'; | |
| }); | |
| stopBtn.addEventListener('click', () => { | |
| recognition.stop(); | |
| startBtn.disabled = false; | |
| stopBtn.disabled = true; | |
| status.textContent = 'Status: Stopped'; | |
| }); | |
| recognition.addEventListener('result', (event) => { | |
| const command = event.results[0][0].transcript; | |
| handleCommand(command); | |
| }); | |
| recognition.addEventListener('end', () => { | |
| startBtn.disabled = false; | |
| stopBtn.disabled = true; | |
| status.textContent = 'Status: Ready'; | |
| }); | |
| recognition.addEventListener('error', (event) => { | |
| status.textContent = `Status: Error - ${event.error}`; | |
| startBtn.disabled = false; | |
| stopBtn.disabled = true; | |
| }); | |
| // Voice setting controls | |
| rate.addEventListener('input', () => { | |
| rateValue.textContent = rate.value; | |
| }); | |
| pitch.addEventListener('input', () => { | |
| pitchValue.textContent = pitch.value; | |
| }); | |
| </script> | |
| </div> | |
| </body> | |
| </html> |