├── .DS_Store ├── README.md ├── TODO.md ├── assets ├── .DS_Store ├── banner.png ├── bannerBG.png ├── icon.png ├── icons │ ├── .DS_Store │ ├── pauseIcon.svg │ ├── resumeIcon.svg │ ├── speakIcon.svg │ └── stopIcon.svg ├── screenshots │ ├── Screenshot 2021-09-05 at 12.25.52 PM.png │ ├── Screenshot 2021-09-05 at 12.26.15 PM.png │ └── Screenshot 2021-09-05 at 12.26.25 PM.png └── whiteGlossBackground.png ├── index.html ├── index.js └── style.css /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virejdasani/OfflineTextToSpeech/43ed481128248aaf6a975bf28cbc71b8dfa8c9af/.DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Offline Text To Speech](https://virejdasani.github.io/OfflineTextToSpeech/) 2 | ## Also check out [Offline Text To Speech Browser Extension](https://virejdasani.github.io/OfflineTextToSpeech-Extension/) 3 | ![](https://raw.githubusercontent.com/virejdasani/OfflineTextToSpeech/main/assets/bannerBG.png) 4 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | Add dark mode 2 | -------------------------------------------------------------------------------- /assets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virejdasani/OfflineTextToSpeech/43ed481128248aaf6a975bf28cbc71b8dfa8c9af/assets/.DS_Store -------------------------------------------------------------------------------- /assets/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virejdasani/OfflineTextToSpeech/43ed481128248aaf6a975bf28cbc71b8dfa8c9af/assets/banner.png -------------------------------------------------------------------------------- /assets/bannerBG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virejdasani/OfflineTextToSpeech/43ed481128248aaf6a975bf28cbc71b8dfa8c9af/assets/bannerBG.png -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virejdasani/OfflineTextToSpeech/43ed481128248aaf6a975bf28cbc71b8dfa8c9af/assets/icon.png -------------------------------------------------------------------------------- /assets/icons/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virejdasani/OfflineTextToSpeech/43ed481128248aaf6a975bf28cbc71b8dfa8c9af/assets/icons/.DS_Store -------------------------------------------------------------------------------- /assets/icons/pauseIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 10 | 14 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /assets/icons/resumeIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 10 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /assets/icons/speakIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 10 | 15 | 20 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /assets/icons/stopIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 10 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /assets/screenshots/Screenshot 2021-09-05 at 12.25.52 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virejdasani/OfflineTextToSpeech/43ed481128248aaf6a975bf28cbc71b8dfa8c9af/assets/screenshots/Screenshot 2021-09-05 at 12.25.52 PM.png -------------------------------------------------------------------------------- /assets/screenshots/Screenshot 2021-09-05 at 12.26.15 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virejdasani/OfflineTextToSpeech/43ed481128248aaf6a975bf28cbc71b8dfa8c9af/assets/screenshots/Screenshot 2021-09-05 at 12.26.15 PM.png -------------------------------------------------------------------------------- /assets/screenshots/Screenshot 2021-09-05 at 12.26.25 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virejdasani/OfflineTextToSpeech/43ed481128248aaf6a975bf28cbc71b8dfa8c9af/assets/screenshots/Screenshot 2021-09-05 at 12.26.25 PM.png -------------------------------------------------------------------------------- /assets/whiteGlossBackground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virejdasani/OfflineTextToSpeech/43ed481128248aaf6a975bf28cbc71b8dfa8c9af/assets/whiteGlossBackground.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 13 | 23 | 24 | 29 | 30 | 31 | 32 | 33 | 34 | Offline Text To Speech 35 | 36 | 37 | 38 |
39 | 40 | 41 | 42 |
43 | 44 |

Offline Text To Speech

45 |
46 | 47 |

48 | Offline Text To Speech is now available as a browser extension! 49 |

50 |

51 | Download it here 54 |

55 |
56 | 57 | 63 | 64 |
65 | 66 | 67 | 68 |
69 | 79 | 80 |

|

81 | 82 | 92 | 93 |

|

94 | 95 | 101 |
102 | 103 | 138 | 139 |
140 | 157 |
158 |
159 |
160 | 161 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // This is so that if speech is still playing from previous session, it stops on page load 2 | speechSynthesis.cancel(); 3 | 4 | var isSpeaking = false; 5 | 6 | // Initialize the speech synthesis 7 | var speech = new SpeechSynthesisUtterance(); 8 | speech.rate = 1; 9 | speech.pitch = 1; 10 | speech.volume = 1; 11 | speech.voice = speechSynthesis.getVoices()[0]; 12 | 13 | function speakInputText() { 14 | isSpeaking = true; 15 | 16 | speech.text = document.getElementById("textInput").value; 17 | speechSynthesis.speak(speech); 18 | } 19 | 20 | function pauseSpeech() { 21 | if (isSpeaking) { 22 | isSpeaking = false; 23 | speechSynthesis.pause(); 24 | document.getElementById( 25 | "pauseButton" 26 | ).innerHTML = `Resume`; 28 | } else { 29 | isSpeaking = true; 30 | speechSynthesis.resume(); 31 | document.getElementById( 32 | "pauseButton" 33 | ).innerHTML = `Pause`; 35 | } 36 | } 37 | 38 | function stopSpeech() { 39 | isSpeaking = false; 40 | speechSynthesis.cancel(); 41 | } 42 | 43 | function changeVoice(voice) { 44 | if (voice == "voice1") { 45 | // console.log((speech.voice = speechSynthesis.getVoices()[8])); 46 | speech.voice = speechSynthesis.getVoices()[8]; 47 | } else if (voice == "voice2") { 48 | // console.log((speech.voice = speechSynthesis.getVoices()[0])); 49 | speech.voice = speechSynthesis.getVoices()[0]; 50 | } else if (voice == "voice3") { 51 | // console.log((speech.voice = speechSynthesis.getVoices()[1])); 52 | speech.voice = speechSynthesis.getVoices()[1]; 53 | } else if (voice == "voice4") { 54 | // console.log((speech.voice = speechSynthesis.getVoices()[11])); 55 | speech.voice = speechSynthesis.getVoices()[11]; 56 | } else if (voice == "voice5") { 57 | // console.log((speech.voice = speechSynthesis.getVoices()[12])); 58 | speech.voice = speechSynthesis.getVoices()[12]; 59 | } else if (voice == "voice6") { 60 | // console.log((speech.voice = speechSynthesis.getVoices()[18])); 61 | speech.voice = speechSynthesis.getVoices()[18]; 62 | } else if (voice == "voice7") { 63 | // console.log((speech.voice = speechSynthesis.getVoices()[33])); 64 | speech.voice = speechSynthesis.getVoices()[33]; 65 | } else if (voice == "voice8") { 66 | // console.log((speech.voice = speechSynthesis.getVoices()[37])); 67 | speech.voice = speechSynthesis.getVoices()[37]; 68 | } else if (voice == "voice9") { 69 | // console.log((speech.voice = speechSynthesis.getVoices()[41])); 70 | speech.voice = speechSynthesis.getVoices()[41]; 71 | } 72 | 73 | // for (let i = 0; i < 100; i++) { 74 | // console.log((speech.voice = speechSynthesis.getVoices()[i])); 75 | // } 76 | } 77 | 78 | function changeVoiceSpeed(voiceSpeed) { 79 | // For some reason, speed below 0.5 doesn't work 80 | if (voiceSpeed == "speed2") { 81 | speech.rate = 2; 82 | } else if (voiceSpeed == "speed1.75") { 83 | speech.rate = 1.75; 84 | } else if (voiceSpeed == "speed1.5") { 85 | speech.rate = 1.5; 86 | } else if (voiceSpeed == "speed1.25") { 87 | speech.rate = 1.25; 88 | } else if (voiceSpeed == "speed1") { 89 | speech.rate = 1; 90 | } else if (voiceSpeed == "speed0.75") { 91 | speech.rate = 0.75; 92 | } else if (voiceSpeed == "speed0.5") { 93 | speech.rate = 0.5; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@350;400&display=swap"); 2 | 3 | :root { 4 | --body-font-size: 18px; 5 | --body-font-weight: 300; 6 | --header-font-weight: 500; 7 | 8 | --main-lateral-padding: 17px; 9 | 10 | --footer-color: #bbbbbb; 11 | --link-color: #248ef1; 12 | --link-underline-color: #248ef1e5; 13 | 14 | --small-link-color: #3f3f3f; 15 | } 16 | 17 | 18 | * { 19 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif; 20 | } 21 | 22 | body { 23 | padding-left: 0px; 24 | padding-right: 0px; 25 | margin: auto; 26 | background-image: url("./assets/whiteGlossBackground.png"); 27 | } 28 | 29 | .main { 30 | margin: auto; 31 | width: 100%; 32 | } 33 | 34 | .padding { 35 | padding: 12px; 36 | } 37 | 38 | .padding-semi-large { 39 | padding: 14px 0px; 40 | } 41 | 42 | h2 { 43 | font-size: 34px; 44 | font-weight: var(--header-font-weight); 45 | text-align: left; 46 | line-height: 125%; 47 | margin: auto; 48 | } 49 | 50 | p { 51 | font-size: 18px; 52 | line-height: 160%; 53 | margin: auto; 54 | font-weight: var(--body-font-weight); 55 | } 56 | 57 | #indicators { 58 | display: flex; 59 | flex-direction: row; 60 | padding-top: 20px; 61 | align-items: center; 62 | } 63 | 64 | #repo-links { 65 | display: flex; 66 | flex-direction: row; 67 | align-items: center; 68 | } 69 | 70 | .indicator-separator { 71 | padding-left: 7px; 72 | padding-right: 7px; 73 | } 74 | 75 | .indicator { 76 | font-size: 16px; 77 | color: #00000059; 78 | font-weight: 400; 79 | } 80 | 81 | #indicators a { 82 | text-underline-offset: 1px; 83 | transition: 0.2s; 84 | } 85 | 86 | #indicators a:hover { 87 | color: var(--small-link-color); 88 | } 89 | 90 | .icon-link { 91 | width: 16px; 92 | padding-left: 7px; 93 | } 94 | 95 | .icon-link-small { 96 | width: 14px; 97 | padding-left: 7px; 98 | } 99 | 100 | .link { 101 | color: var(--link-color); 102 | text-decoration: none; 103 | padding-bottom: 3px; 104 | 105 | background: linear-gradient(to right, transparent, transparent), linear-gradient(to right, var(--link-underline-color), var(--link-underline-color)); 106 | background-size: 100% 3px, 0 3px; 107 | background-position: 100% 100%, 0 100%; 108 | background-repeat: no-repeat; 109 | transition: background-size 500ms; 110 | } 111 | 112 | .link:hover { 113 | background-size: 0 3px, 100% 3px; 114 | } 115 | 116 | .button-standard { 117 | display: flex; 118 | flex-direction: row; 119 | justify-content: center; 120 | align-items: center; 121 | 122 | padding: 10px; 123 | padding-left: 11px; 124 | padding-right: 11px; 125 | background-color: rgba(255, 255, 255, 0.4); 126 | 127 | border-color: rgba(0, 0, 0, 0.14); 128 | border-width: 1px; 129 | border-style: solid; 130 | border-radius: 6px; 131 | text-decoration: none; 132 | color: #000000; 133 | 134 | box-sizing: border-box; 135 | box-shadow: 0px 3px 11px rgba(0, 0, 0, 0.07); 136 | font-size: 16px; 137 | font-weight: var(--header-font-weight); 138 | 139 | transition: all 0.15s ease; 140 | } 141 | 142 | .button-standard:visited { 143 | color: inherit; 144 | } 145 | 146 | .button-standard:hover { 147 | transform: scale(1.08); 148 | background-color: rgba(255, 255, 255, 0.7); 149 | } 150 | 151 | #section-1 { 152 | padding-top: 50px; 153 | } 154 | 155 | #app-store-buttons { 156 | display: flex; 157 | flex-direction: row; 158 | justify-content: center; 159 | } 160 | 161 | #app-store-buttons p { 162 | font-size: 24px; 163 | opacity: 0.1; 164 | line-height: 0%; 165 | padding: 15px; 166 | } 167 | 168 | .section-alignment { 169 | display: flex; 170 | justify-content: center; 171 | align-items: center; 172 | flex-direction: column; 173 | } 174 | 175 | #landing-header { 176 | text-align: center; 177 | padding-top: 7px; 178 | } 179 | 180 | .main { 181 | max-width: 750px; 182 | } 183 | 184 | /* Mobile Only */ 185 | @media only screen and (max-width: 425px) { 186 | body { 187 | background-size: 1200%; 188 | } 189 | 190 | h2 { 191 | font-size: 36px; 192 | } 193 | 194 | p { 195 | font-size: 20px; 196 | } 197 | 198 | #app-store-buttons { 199 | display: contents; 200 | } 201 | } 202 | 203 | /* Mobile Design - Tablet */ 204 | @media only screen and (max-width: 999px) { 205 | body { 206 | background-size: 1200%; 207 | } 208 | 209 | /* Make sure to show the links once we're on a desktop */ 210 | 211 | /* Make buttons stack instead */ 212 | /**/ 213 | 214 | #app-store-buttons p { 215 | padding: 8px; 216 | visibility: hidden; 217 | } 218 | } 219 | 220 | /* Desktop Design */ 221 | @media only screen and (min-width: 1000px) { 222 | body { 223 | background-size: 700%; 224 | } 225 | 226 | /* Make sure to show the links once we're on a desktop */ 227 | 228 | /* Push out the lists for better visibility */ 229 | } 230 | 231 | textarea { 232 | padding: 10px; 233 | border-radius: 10px; 234 | background-color: rgba(255, 255, 255, 0.1); 235 | width: 80%; 236 | font-size: 16px; 237 | margin-top: 8px; 238 | } 239 | 240 | .optionsDiv { 241 | position: relative; 242 | /*Don't really need this just for demo styling*/ 243 | 244 | float: left; 245 | min-width: 200px; 246 | margin-top: 20px; 247 | margin-left: 10px; 248 | margin-right: 10px; 249 | } 250 | 251 | .optionsDiv:after { 252 | content: '<>'; 253 | font: 17px "Consolas", monospace; 254 | color: #333; 255 | -webkit-transform: rotate(90deg); 256 | -moz-transform: rotate(90deg); 257 | -ms-transform: rotate(90deg); 258 | transform: rotate(90deg); 259 | right: 11px; 260 | top: 14px; 261 | padding: 0 0 2px; 262 | border-bottom: 1px solid #999; 263 | position: absolute; 264 | pointer-events: none; 265 | } 266 | 267 | .optionsDiv select { 268 | -webkit-appearance: none; 269 | -moz-appearance: none; 270 | appearance: none; 271 | display: block; 272 | width: 100%; 273 | max-width: 320px; 274 | height: 40px; 275 | float: right; 276 | margin: 5px 0px; 277 | padding: 0px 24px; 278 | font-size: 16px; 279 | line-height: 1.75; 280 | color: #333; 281 | background-color: rgba(255, 255, 255, 0.3); 282 | background-image: none; 283 | border: 1px solid #cccccc; 284 | -ms-word-break: normal; 285 | word-break: normal; 286 | border-radius: 6px; 287 | } 288 | 289 | #dropdowns { 290 | margin: 10px; 291 | } 292 | 293 | /* This it the input field when it does not have focus */ 294 | textarea[type=text] { 295 | outline: none; 296 | box-shadow: none; 297 | border: solid 1px rgb(91, 91, 91); 298 | } 299 | 300 | /* This is for the highlighted color when the input field is in focus */ 301 | textarea[type=text]:focus { 302 | outline: none; 303 | box-shadow: none; 304 | border: solid 1px rgb(125, 125, 125); 305 | } 306 | 307 | /* This it the input field when it does not have focus */ 308 | select { 309 | outline: none; 310 | box-shadow: none; 311 | border: solid 1px rgb(91, 91, 91); 312 | } 313 | 314 | /* This is for the highlighted color when the input field is in focus */ 315 | select:focus { 316 | outline: none; 317 | box-shadow: none; 318 | border: solid 1px rgb(125, 125, 125); 319 | } 320 | 321 | ::-webkit-scrollbar { 322 | background-color: rgba(255, 255, 255, 0.4); 323 | width: 10px !important; 324 | border-radius: 10px; 325 | } --------------------------------------------------------------------------------