Skip to content

Commit

Permalink
speaking number of balls
Browse files Browse the repository at this point in the history
  • Loading branch information
ToonTalk committed Dec 16, 2024
1 parent 98cae91 commit d9d3e89
Showing 1 changed file with 262 additions and 0 deletions.
262 changes: 262 additions & 0 deletions apps/speaking number.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Speech to Numbers</title>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
margin: 0;
padding: 0;
background-color: #f4f4f9;
color: #333;
}

header {
background-color: #6200ea;
color: white;
padding: 1rem;
margin-bottom: 20px;
}

h1 {
margin: 0;
}

#last-spoken {
font-size: 1.5rem;
margin-top: 10px;
}

#numbers {
font-size: 2rem;
margin-top: 20px;
}

#ball-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
margin-top: 20px;
}

.ball {
width: 50px;
height: 50px;
border-radius: 50%;
margin: 5px;
}

button {
background-color: #6200ea;
color: white;
border: none;
padding: 10px 20px;
font-size: 1rem;
cursor: pointer;
border-radius: 5px;
margin-top: 20px;
}

button:disabled {
background-color: #ccc;
cursor: not-allowed;
}
</style>
</head>
<body>
<header>
<h1>Speech to Numbers</h1>
</header>
<main>
<p>Click the button below and start speaking. Any numbers you say will appear here:</p>
<div id="last-spoken">Last spoken: None</div>
<div id="numbers">Last number: No numbers detected yet.</div>
<div id="ball-container"></div>
<button id="start-btn">Start Listening</button>
</main>
<script>
const startButton = document.getElementById('start-btn');
const lastSpokenDiv = document.getElementById('last-spoken');
const numbersDiv = document.getElementById('numbers');
const ballContainer = document.getElementById('ball-container');

const numberWords = {
one: 1,
two: 2,
three: 3,
four: 4,
five: 5,
six: 6,
seven: 7,
eight: 8,
nine: 9,
ten: 10
};

let ballCounts = [];
let lastProcessedResult = ""; // Store last processed result to avoid duplicates
let isRecognitionStopped = false;
let isAnswerPhase = false; // Toggle to differentiate between normal and answer phases

if ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window) {
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
const recognition = new SpeechRecognition();

recognition.lang = 'en-US';
recognition.interimResults = true;
recognition.continuous = true;

let isListening = false;

startButton.addEventListener('click', () => {
if (isListening) {
recognition.stop();
startButton.textContent = 'Start Listening';
isListening = false;
} else {
try {
lastProcessedResult = ""; // Reset processed result tracking
recognition.start();
startButton.textContent = 'Listening... Click to Stop';
isListening = true;
} catch (error) {
console.error('Error starting recognition:', error);
alert('Please allow microphone permissions and reload the page.');
}
}
});

recognition.onresult = (event) => {
let interimTranscript = "";

for (let i = event.resultIndex; i < event.results.length; i++) {
const result = event.results[i];
const transcript = result[0].transcript.toLowerCase();

// Update "Last spoken" regardless of phase
updateLastSpoken(transcript, result.isFinal);

if (result.isFinal) {
// Handle either normal or answer phase
if (!isAnswerPhase) {
if (transcript !== lastProcessedResult) {
lastProcessedResult = transcript; // Prevent duplicates
processTranscript(transcript);
}
} else {
handleAnswer(transcript);
}
}
}
};

function updateLastSpoken(text, isFinal) {
const prefix = isFinal ? "Last spoken" : "Last spoken (interim)";
lastSpokenDiv.textContent = `${prefix}: ${text}`;
}

function processTranscript(transcript) {
// Convert number words to digits
transcript = transcript.replace(/\b(one|two|three|four|five|six|seven|eight|nine|ten)\b/g, (match) => {
return numberWords[match];
});

const matches = transcript.match(/\b\d+\b/g);

if (matches) {
const latestNumber = matches[matches.length - 1];
numbersDiv.textContent = `Last number: ${latestNumber}`;
const count = parseInt(latestNumber);

if (!isNaN(count) && count > 0 && ballCounts.length < 2) {
ballCounts.push(count);
addBalls(count);

if (ballCounts.length === 2) {
setTimeout(() => askQuestion(), 500);
}
}
}
}

function addBalls(count) {
const colors = ['#ff5733', '#33ff57', '#3357ff', '#ff33a8', '#a833ff', '#33fff3', '#ffa733'];
const color = colors[Math.floor(Math.random() * colors.length)];

for (let i = 0; i < count; i++) {
const ball = document.createElement('div');
ball.className = 'ball';
ball.style.backgroundColor = color;
ballContainer.appendChild(ball);
}
}

function askQuestion() {
const total = ballCounts.reduce((a, b) => a + b, 0);
const question = `How many balls are there in total?`;

const synth = window.speechSynthesis;
const utterance = new SpeechSynthesisUtterance(question);

isAnswerPhase = true; // Enter answer phase
isRecognitionStopped = true;
recognition.stop();
synth.speak(utterance);

utterance.onend = () => {
isRecognitionStopped = false;
recognition.start();
};
}

function handleAnswer(transcript) {
const total = ballCounts.reduce((a, b) => a + b, 0);
const answer = parseInt(transcript);
const synth = window.speechSynthesis;

if (!isNaN(answer)) {
if (answer === total) {
const congrats = `Good job! You figured out that ${ballCounts[0]} plus ${ballCounts[1]} is ${total}.`;
const congratsUtterance = new SpeechSynthesisUtterance(congrats);
synth.speak(congratsUtterance);
congratsUtterance.onend = () => {
resetToListeningMode();
};
} else {
const hint = `Not quite! Here's a hint: ${ballCounts[0]} plus ${ballCounts[1]} equals ?`;
const hintUtterance = new SpeechSynthesisUtterance(hint);
synth.speak(hintUtterance);
}
}
}

function resetToListeningMode() {
ballCounts = [];
isAnswerPhase = false;
lastProcessedResult = ""; // Reset processed results
}

recognition.onerror = (event) => {
console.error('Speech recognition error:', event.error);
alert('Error with speech recognition. Please reload the page.');
isListening = false;
startButton.textContent = 'Start Listening';
};

recognition.onend = () => {
if (isListening && !isRecognitionStopped) {
recognition.start();
} else {
startButton.textContent = 'Start Listening';
}
};
} else {
alert('Sorry, your browser does not support the Web Speech API. Please use a modern browser like Chrome or Edge.');
startButton.disabled = true;
}
</script>
</body>
</html>

0 comments on commit d9d3e89

Please sign in to comment.