├── .github └── FUNDING.yml ├── .gitattributes ├── images ├── mic.gif ├── mic-slash.gif └── mic-animation.gif ├── style └── web-speech.css ├── README.md ├── LICENSE ├── index.html └── js ├── languages.js └── web-speech-api.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: bensonruan 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /images/mic.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bensonruan/Chrome-Web-Speech-API/HEAD/images/mic.gif -------------------------------------------------------------------------------- /images/mic-slash.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bensonruan/Chrome-Web-Speech-API/HEAD/images/mic-slash.gif -------------------------------------------------------------------------------- /images/mic-animation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bensonruan/Chrome-Web-Speech-API/HEAD/images/mic-animation.gif -------------------------------------------------------------------------------- /style/web-speech.css: -------------------------------------------------------------------------------- 1 | #info a { 2 | color: darkred; 3 | text-decoration: underline; 4 | } 5 | 6 | .final { 7 | color: black; 8 | padding-right: 3px; 9 | } 10 | .interim { 11 | color: gray; 12 | } 13 | #results { 14 | font-size: 14px; 15 | font-weight: bold; 16 | border: 1px solid #ddd; 17 | padding: 15px; 18 | text-align: left; 19 | min-height: 150px; 20 | margin: 0 0 20px 0; 21 | } 22 | #start_button { 23 | border: 0; 24 | background-color:transparent; 25 | padding: 0; 26 | cursor: pointer; 27 | } 28 | #start_img { 29 | width: 50px; 30 | height: 50px; 31 | } 32 | 33 | .btn-primary { 34 | width: 100px; 35 | margin: auto; 36 | } 37 | 38 | select { 39 | padding: 5px 5px; 40 | } 41 | 42 | @media screen and (max-width: 767px) { 43 | #select_dialect { 44 | position: absolute; 45 | right: 0; 46 | } 47 | } 48 | 49 | @media screen and (min-width: 768px) { 50 | select { 51 | margin-right: 10px; 52 | } 53 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Chrome Web Speech API 2 | Voice to Text with Google Chrome Web Speech API 3 | 4 | 5 | ## Live Demo 6 | **[https://bensonruan.com/voice-to-text-with-chrome-web-speech-api/](https://bensonruan.com/voice-to-text-with-chrome-web-speech-api/)** 7 | 8 | 9 | ## Installing 10 | Clone this repository to your local computer 11 | ``` bash 12 | git https://github.com/bensonruan/Chrome-Web-Speech-API.git 13 | ``` 14 | Point your localhost to the cloned root directory 15 | 16 | Browse to http://localhost/index.html with Chrome browser. 17 | 18 | ## Start Typing with your voice 19 | * Turn on the microphone by click on the microphone icon 20 | * Allowing the browser to access your microphone 21 | * Start speaking 22 | 23 | ## Supported Browsers 24 | At the moment, it only support Google Chrome version 25 or later on desktop and Android mobile 25 | 26 | ## Library 27 | * [jquery](https://code.jquery.com/jquery-3.3.1.min.js) - JQuery 28 | 29 | ## Support me 30 | [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/W7W6METMY) 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Benson Ruan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Web Speech APIU 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

Web Speech API Demo

15 |
16 |
17 | 19 |
20 |
21 | 22 | 23 |

24 |

25 |
26 |
27 | 28 | 29 |
30 |
31 |
32 | 33 |
34 |
35 |
36 | 37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /js/languages.js: -------------------------------------------------------------------------------- 1 | var langs = 2 | [['Afrikaans', ['af-ZA']], 3 | ['Bahasa Indonesia',['id-ID']], 4 | ['Bahasa Melayu', ['ms-MY']], 5 | ['Català', ['ca-ES']], 6 | ['Čeština', ['cs-CZ']], 7 | ['Deutsch', ['de-DE']], 8 | ['English', ['en-AU', 'Australia'], 9 | ['en-CA', 'Canada'], 10 | ['en-IN', 'India'], 11 | ['en-NZ', 'New Zealand'], 12 | ['en-ZA', 'South Africa'], 13 | ['en-GB', 'United Kingdom'], 14 | ['en-US', 'United States']], 15 | ['Español', ['es-AR', 'Argentina'], 16 | ['es-BO', 'Bolivia'], 17 | ['es-CL', 'Chile'], 18 | ['es-CO', 'Colombia'], 19 | ['es-CR', 'Costa Rica'], 20 | ['es-EC', 'Ecuador'], 21 | ['es-SV', 'El Salvador'], 22 | ['es-ES', 'España'], 23 | ['es-US', 'Estados Unidos'], 24 | ['es-GT', 'Guatemala'], 25 | ['es-HN', 'Honduras'], 26 | ['es-MX', 'México'], 27 | ['es-NI', 'Nicaragua'], 28 | ['es-PA', 'Panamá'], 29 | ['es-PY', 'Paraguay'], 30 | ['es-PE', 'Perú'], 31 | ['es-PR', 'Puerto Rico'], 32 | ['es-DO', 'República Dominicana'], 33 | ['es-UY', 'Uruguay'], 34 | ['es-VE', 'Venezuela']], 35 | ['Euskara', ['eu-ES']], 36 | ['Français', ['fr-FR']], 37 | ['Galego', ['gl-ES']], 38 | ['Hrvatski', ['hr_HR']], 39 | ['IsiZulu', ['zu-ZA']], 40 | ['Íslenska', ['is-IS']], 41 | ['Italiano', ['it-IT', 'Italia'], 42 | ['it-CH', 'Svizzera']], 43 | ['Magyar', ['hu-HU']], 44 | ['Nederlands', ['nl-NL']], 45 | ['Norsk bokmål', ['nb-NO']], 46 | ['Polski', ['pl-PL']], 47 | ['Português', ['pt-BR', 'Brasil'], 48 | ['pt-PT', 'Portugal']], 49 | ['Română', ['ro-RO']], 50 | ['Slovenčina', ['sk-SK']], 51 | ['Suomi', ['fi-FI']], 52 | ['Svenska', ['sv-SE']], 53 | ['Türkçe', ['tr-TR']], 54 | ['български', ['bg-BG']], 55 | ['Pусский', ['ru-RU']], 56 | ['Српски', ['sr-RS']], 57 | ['한국어', ['ko-KR']], 58 | ['中文', ['cmn-Hans-CN', '普通话 (中国大陆)'], 59 | ['cmn-Hans-HK', '普通话 (香港)'], 60 | ['cmn-Hant-TW', '中文 (台灣)'], 61 | ['yue-Hant-HK', '粵語 (香港)']], 62 | ['日本語', ['ja-JP']], 63 | ['Lingua latīna', ['la']]]; -------------------------------------------------------------------------------- /js/web-speech-api.js: -------------------------------------------------------------------------------- 1 | var messages = { 2 | "start": { 3 | msg: 'Click on the microphone icon and begin speaking.', 4 | class: 'alert-success'}, 5 | "speak_now": { 6 | msg: 'Speak now.', 7 | class: 'alert-success'}, 8 | "no_speech": { 9 | msg: 'No speech was detected. You may need to adjust your microphone settings.', 10 | class: 'alert-danger'}, 11 | "no_microphone": { 12 | msg: 'No microphone was found. Ensure that a microphone is installed and that microphone settings are configured correctly.', 13 | class: 'alert-danger'}, 14 | "allow": { 15 | msg: 'Click the "Allow" button above to enable your microphone.', 16 | class: 'alert-warning'}, 17 | "denied": { 18 | msg: 'Permission to use microphone was denied.', 19 | class: 'alert-danger'}, 20 | "blocked": { 21 | msg: 'Permission to use microphone is blocked. To change, go to chrome://settings/content/microphone', 22 | class: 'alert-danger'}, 23 | "upgrade": { 24 | msg: 'Web Speech API is not supported by this browser. It is only supported by Chrome version 25 or later on desktop and Android mobile.', 25 | class: 'alert-danger'}, 26 | "stop": { 27 | msg: 'Stop listening, click on the microphone icon to restart', 28 | class: 'alert-success'}, 29 | "copy": { 30 | msg: 'Content copy to clipboard successfully.', 31 | class: 'alert-success'}, 32 | } 33 | 34 | var final_transcript = ''; 35 | var recognizing = false; 36 | var ignore_onend; 37 | var start_timestamp; 38 | var recognition; 39 | 40 | $( document ).ready(function() { 41 | for (var i = 0; i < langs.length; i++) { 42 | select_language.options[i] = new Option(langs[i][0], i); 43 | } 44 | select_language.selectedIndex = 6; 45 | updateCountry(); 46 | select_dialect.selectedIndex = 6; 47 | 48 | if (!('webkitSpeechRecognition' in window)) { 49 | upgrade(); 50 | } else { 51 | showInfo('start'); 52 | start_button.style.display = 'inline-block'; 53 | recognition = new webkitSpeechRecognition(); 54 | recognition.continuous = true; 55 | recognition.interimResults = true; 56 | 57 | recognition.onstart = function() { 58 | recognizing = true; 59 | showInfo('speak_now'); 60 | start_img.src = 'images/mic-animation.gif'; 61 | }; 62 | 63 | recognition.onerror = function(event) { 64 | if (event.error == 'no-speech') { 65 | start_img.src = 'images/mic.gif'; 66 | showInfo('no_speech'); 67 | ignore_onend = true; 68 | } 69 | if (event.error == 'audio-capture') { 70 | start_img.src = 'images/mic.gif'; 71 | showInfo('no_microphone'); 72 | ignore_onend = true; 73 | } 74 | if (event.error == 'not-allowed') { 75 | if (event.timeStamp - start_timestamp < 100) { 76 | showInfo('blocked'); 77 | } else { 78 | showInfo('denied'); 79 | } 80 | ignore_onend = true; 81 | } 82 | }; 83 | 84 | recognition.onend = function() { 85 | recognizing = false; 86 | if (ignore_onend) { 87 | return; 88 | } 89 | start_img.src = 'images/mic.gif'; 90 | if (!final_transcript) { 91 | showInfo('start'); 92 | return; 93 | } 94 | showInfo('stop'); 95 | if (window.getSelection) { 96 | window.getSelection().removeAllRanges(); 97 | var range = document.createRange(); 98 | range.selectNode(document.getElementById('final_span')); 99 | window.getSelection().addRange(range); 100 | } 101 | }; 102 | 103 | recognition.onresult = function(event) { 104 | var interim_transcript = ''; 105 | for (var i = event.resultIndex; i < event.results.length; ++i) { 106 | if (event.results[i].isFinal) { 107 | final_transcript += event.results[i][0].transcript; 108 | } else { 109 | interim_transcript += event.results[i][0].transcript; 110 | } 111 | } 112 | final_transcript = capitalize(final_transcript); 113 | final_span.innerHTML = linebreak(final_transcript); 114 | interim_span.innerHTML = linebreak(interim_transcript); 115 | }; 116 | } 117 | }); 118 | 119 | 120 | function updateCountry() { 121 | for (var i = select_dialect.options.length - 1; i >= 0; i--) { 122 | select_dialect.remove(i); 123 | } 124 | var list = langs[select_language.selectedIndex]; 125 | for (var i = 1; i < list.length; i++) { 126 | select_dialect.options.add(new Option(list[i][1], list[i][0])); 127 | } 128 | select_dialect.style.visibility = list[1].length == 1 ? 'hidden' : 'visible'; 129 | } 130 | 131 | 132 | function upgrade() { 133 | start_button.style.visibility = 'hidden'; 134 | showInfo('upgrade'); 135 | } 136 | 137 | var two_line = /\n\n/g; 138 | var one_line = /\n/g; 139 | function linebreak(s) { 140 | return s.replace(two_line, '

').replace(one_line, '
'); 141 | } 142 | 143 | var first_char = /\S/; 144 | function capitalize(s) { 145 | return s.replace(first_char, function(m) { return m.toUpperCase(); }); 146 | } 147 | 148 | $("#copy_button").click(function () { 149 | if (recognizing) { 150 | recognizing = false; 151 | recognition.stop(); 152 | } 153 | setTimeout(copyToClipboard, 500); 154 | 155 | }); 156 | 157 | function copyToClipboard() { 158 | if (document.selection) { 159 | var range = document.body.createTextRange(); 160 | range.moveToElementText(document.getElementById('results')); 161 | range.select().createTextRange(); 162 | document.execCommand("copy"); 163 | 164 | } else if (window.getSelection) { 165 | var range = document.createRange(); 166 | range.selectNode(document.getElementById('results')); 167 | window.getSelection().addRange(range); 168 | document.execCommand("copy"); 169 | } 170 | showInfo('copy'); 171 | } 172 | 173 | $("#start_button").click(function () { 174 | if (recognizing) { 175 | recognition.stop(); 176 | return; 177 | } 178 | final_transcript = ''; 179 | recognition.lang = select_dialect.value; 180 | recognition.start(); 181 | ignore_onend = false; 182 | final_span.innerHTML = ''; 183 | interim_span.innerHTML = ''; 184 | start_img.src = 'images/mic-slash.gif'; 185 | showInfo('allow'); 186 | start_timestamp = event.timeStamp; 187 | }); 188 | 189 | $("#select_language").change(function () { 190 | updateCountry(); 191 | }); 192 | 193 | function showInfo(s) { 194 | if (s) { 195 | var message = messages[s]; 196 | $("#info").html(message.msg); 197 | $("#info").removeClass(); 198 | $("#info").addClass('alert'); 199 | $("#info").addClass(message.class); 200 | } else { 201 | $("#info").removeClass(); 202 | $("#info").addClass('d-none'); 203 | } 204 | } 205 | --------------------------------------------------------------------------------