-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Add on-device speech recognition demo #332
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
8df754f
1d16823
88f1aab
093c47b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="utf-8" /> | ||
<meta name="viewport" content="width=device-width" /> | ||
|
||
<title>On-device speech color changer</title> | ||
|
||
<link rel="stylesheet" href="style.css" /> | ||
<script src="script.js" defer></script> | ||
</head> | ||
|
||
<body> | ||
<h1>On-device speech color changer</h1> | ||
|
||
<p>This demo works in Chrome 139+ on desktop and equivalent Chromiums.</p> | ||
|
||
<p class="hints"></p> | ||
<p class="output"><em>...diagnostic messages</em></p> | ||
</body> | ||
</html> |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,138 @@ | ||||||
const colors = [ | ||||||
"aqua", | ||||||
"azure", | ||||||
"bisque", | ||||||
"blue", | ||||||
"brown", | ||||||
"chocolate", | ||||||
"coral", | ||||||
"cornflowerblue", | ||||||
"crimson", | ||||||
"cyan", | ||||||
"deepskyblue", | ||||||
"fuchsia", | ||||||
"ghostwhite", | ||||||
"gold", | ||||||
"goldenrod", | ||||||
"gray", | ||||||
"green", | ||||||
"greenyellow", | ||||||
"hotpink", | ||||||
"indigo", | ||||||
"ivory", | ||||||
"khaki", | ||||||
"lavender", | ||||||
"lightseagreen", | ||||||
"lime", | ||||||
"linen", | ||||||
"magenta", | ||||||
"maroon", | ||||||
"moccasin", | ||||||
"navy", | ||||||
"olive", | ||||||
"orange", | ||||||
"orchid", | ||||||
"peru", | ||||||
"pink", | ||||||
"plum", | ||||||
"purple", | ||||||
"rebeccapurple", | ||||||
"red", | ||||||
"salmon", | ||||||
"sienna", | ||||||
"silver", | ||||||
"snow", | ||||||
"steelblue", | ||||||
"tan", | ||||||
"teal", | ||||||
"thistle", | ||||||
"tomato", | ||||||
"turquoise", | ||||||
"violet", | ||||||
"yellow", | ||||||
]; | ||||||
|
||||||
const phraseData = [ | ||||||
{ phrase: "azure", boost: 10.0 }, | ||||||
{ phrase: "khaki", boost: 3.0 }, | ||||||
{ phrase: "tan", boost: 2.0 }, | ||||||
]; | ||||||
|
||||||
const phraseObjects = phraseData.map( | ||||||
(p) => new SpeechRecognitionPhrase(p.phrase, p.boost) | ||||||
); | ||||||
|
||||||
const recognition = new SpeechRecognition(); | ||||||
recognition.continuous = false; | ||||||
recognition.lang = "en-US"; | ||||||
recognition.interimResults = false; | ||||||
recognition.processLocally = true; | ||||||
recognition.phrases = phraseObjects; | ||||||
|
||||||
recognition.phrases.push(new SpeechRecognitionPhrase("thistle", 5.0)); | ||||||
|
||||||
const diagnostic = document.querySelector(".output"); | ||||||
const bg = document.querySelector("html"); | ||||||
const hints = document.querySelector(".hints"); | ||||||
|
||||||
let colorHTML = ""; | ||||||
colors.forEach(function (v, i, a) { | ||||||
console.log(v, i); | ||||||
colorHTML += `<span style="background-color: ${v};">${v}</span> `; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would suggest using buttons to make the demo accessible for keyboard users.
Suggested change
|
||||||
}); | ||||||
hints.innerHTML = `Tap/click then say a color to change the background color of the app. For example, you could try ${colorHTML}`; | ||||||
|
||||||
document.body.addEventListener("click", () => { | ||||||
// check availability of target language | ||||||
SpeechRecognition.available({ langs: ["en-US"], processLocally: true }).then( | ||||||
(result) => { | ||||||
if (result === "unavailable") { | ||||||
diagnostic.textContent = `en-US not available to download at this time. Sorry!`; | ||||||
} else if (result === "available") { | ||||||
recognition.start(); | ||||||
console.log("Ready to receive a color command."); | ||||||
} else { | ||||||
diagnostic.textContent = `en-US language pack downloading`; | ||||||
SpeechRecognition.install({ | ||||||
langs: ["en-US"], | ||||||
processLocally: true, | ||||||
}).then((result) => { | ||||||
if (result) { | ||||||
diagnostic.textContent = `en-US language pack downloaded. Try again.`; | ||||||
} else { | ||||||
diagnostic.textContent = `en-US language pack failed to download. Try again later.`; | ||||||
} | ||||||
}); | ||||||
} | ||||||
} | ||||||
); | ||||||
}); | ||||||
|
||||||
recognition.addEventListener("result", (event) => { | ||||||
// The SpeechRecognitionEvent results property returns a SpeechRecognitionResultList object | ||||||
// The SpeechRecognitionResultList object contains SpeechRecognitionResult objects. | ||||||
// It has a getter so it can be accessed like an array | ||||||
// The first [0] returns the SpeechRecognitionResult at the last position. | ||||||
// Each SpeechRecognitionResult object contains SpeechRecognitionAlternative objects that contain individual results. | ||||||
// These also have getters so they can be accessed like arrays. | ||||||
// The second [0] returns the SpeechRecognitionAlternative at position 0. | ||||||
// We then return the transcript property of the SpeechRecognitionAlternative object | ||||||
Comment on lines
+112
to
+119
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if that prose would be easier to read in a guide/reference where this demo is linked from? And if it’s already there, then we don’t need it here. |
||||||
let color = event.results[0][0].transcript; | ||||||
// Remove whitespace from recognized speech | ||||||
color = color.replace(/\s+/g, ""); | ||||||
diagnostic.textContent = `Result received: ${color}.`; | ||||||
bg.style.backgroundColor = color; | ||||||
console.log(`Confidence: ${event.results[0][0].confidence}`); | ||||||
}); | ||||||
|
||||||
recognition.addEventListener("speechend", () => { | ||||||
recognition.stop(); | ||||||
}); | ||||||
|
||||||
recognition.addEventListener("nomatch", (event) => { | ||||||
diagnostic.textContent = "I didn't recognise that color."; | ||||||
}); | ||||||
|
||||||
recognition.addEventListener("error", (event) => { | ||||||
diagnostic.textContent = `Error occurred in recognition: ${event.error}`; | ||||||
}); |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,43 @@ | ||||||||||||||||||||||||||||||||||||||||||
body, | ||||||||||||||||||||||||||||||||||||||||||
html { | ||||||||||||||||||||||||||||||||||||||||||
margin: 0; | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
html { | ||||||||||||||||||||||||||||||||||||||||||
height: 100%; | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
body { | ||||||||||||||||||||||||||||||||||||||||||
height: inherit; | ||||||||||||||||||||||||||||||||||||||||||
overflow: hidden; | ||||||||||||||||||||||||||||||||||||||||||
max-width: 800px; | ||||||||||||||||||||||||||||||||||||||||||
margin: 0 auto; | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
h1, | ||||||||||||||||||||||||||||||||||||||||||
p { | ||||||||||||||||||||||||||||||||||||||||||
font-family: sans-serif; | ||||||||||||||||||||||||||||||||||||||||||
text-align: center; | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+10
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
They will be inherited from the body anyway. I would suggest setting them like that :) |
||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
.output { | ||||||||||||||||||||||||||||||||||||||||||
height: 100px; | ||||||||||||||||||||||||||||||||||||||||||
margin: 0; | ||||||||||||||||||||||||||||||||||||||||||
overflow: auto; | ||||||||||||||||||||||||||||||||||||||||||
position: absolute; | ||||||||||||||||||||||||||||||||||||||||||
bottom: 0; | ||||||||||||||||||||||||||||||||||||||||||
right: 0; | ||||||||||||||||||||||||||||||||||||||||||
left: 0; | ||||||||||||||||||||||||||||||||||||||||||
background-color: rgba(255 255 255 / 0.2); | ||||||||||||||||||||||||||||||||||||||||||
display: flex; | ||||||||||||||||||||||||||||||||||||||||||
justify-content: center; | ||||||||||||||||||||||||||||||||||||||||||
align-items: center; | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
ul { | ||||||||||||||||||||||||||||||||||||||||||
margin: 0; | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
.hints span { | ||||||||||||||||||||||||||||||||||||||||||
text-shadow: 0px 0px 6px rgba(255 255 255 / 0.7); | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+41
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The demo gave me “Uncaught ReferenceError: SpeechRecognitionPhrase is not defined” error in Chrome 140 but it worked fine in Canary 142. So I guess it would be fair to update it to 142, as the demo now requires additional APIs to work.