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; | |
} | |
.error { | |
background: #ffebee; | |
color: #c62828; | |
padding: 10px; | |
border-radius: 4px; | |
margin: 10px 0; | |
display: none; | |
} | |
</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="error" id="error"></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> | |
// Check if speech recognition is supported | |
if (!('webkitSpeechRecognition' in window) && !('SpeechRecognition' in window)) { | |
document.getElementById('error').style.display = 'block'; | |
document.getElementById('error').textContent = 'Speech recognition is not supported in this browser. Please try Chrome.'; | |
document.getElementById('startBtn').disabled = true; | |
} | |
// Initialize speech recognition | |
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; | |
const recognition = new SpeechRecognition(); | |
// Configure recognition | |
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'); | |
const errorDiv = document.getElementById('error'); | |
// 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) { | |
console.error('speechSynthesis.speaking'); | |
return; | |
} | |
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', () => { | |
errorDiv.style.display = 'none'; | |
try { | |
recognition.start(); | |
startBtn.disabled = true; | |
stopBtn.disabled = false; | |
status.textContent = 'Status: Listening...'; | |
console.log('Recognition started'); | |
} catch (err) { | |
console.error('Recognition error:', err); | |
errorDiv.style.display = 'block'; | |
errorDiv.textContent = `Error starting recognition: ${err.message}`; | |
} | |
}); | |
stopBtn.addEventListener('click', () => { | |
try { | |
recognition.stop(); | |
startBtn.disabled = false; | |
stopBtn.disabled = true; | |
status.textContent = 'Status: Stopped'; | |
} catch (err) { | |
console.error('Stop error:', err); | |
errorDiv.style.display = 'block'; | |
errorDiv.textContent = `Error stopping recognition: ${err.message}`; | |
} | |
}); | |
recognition.onstart = () => { | |
console.log('Recognition started'); | |
status.textContent = 'Status: Listening...'; | |
}; | |
recognition.onresult = (event) => { | |
console.log('Recognition result received'); | |
const command = event.results[0][0].transcript; | |
handleCommand(command); | |
}; | |
recognition.onend = () => { | |
console.log('Recognition ended'); | |
startBtn.disabled = false; | |
stopBtn.disabled = true; | |
status.textContent = 'Status: Ready'; | |
}; | |
recognition.onerror = (event) => { | |
console.error('Recognition error:', event.error); | |
errorDiv.style.display = 'block'; | |
errorDiv.textContent = `Error: ${event.error}. ${event.error === 'not-allowed' ? 'Please ensure microphone permissions are granted.' : ''}`; | |
status.textContent = 'Status: 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> | |
</body> | |
</html> |