Skip to content

Instantly share code, notes, and snippets.

@dotproto
Last active January 8, 2025 06:40
Show Gist options
  • Save dotproto/febf568f177dca9a7cf4ede329e53667 to your computer and use it in GitHub Desktop.
Save dotproto/febf568f177dca9a7cf4ede329e53667 to your computer and use it in GitHub Desktop.
ES2015 Text to Speech bookmarklet (Alt+I). Set up: Copy and paste the contents of this gist into a new bookmark. Use: Select some text and click the bookmarklet to start speaking. Once activated on a page, you can use `Alt+I` or `Alt+1` to speak the selected text. Speech Synthesis API Info: https://developer.mozilla.org/en-US/docs/Web/API/Speech…
{
if (window.runTTS === undefined) {
const MAX_TRIES = 20;
const TRY_DELAY = 100;
/* Adjust voice speed. Default = 1 */
if (navigator.userAgent.includes("Macintosh")) {
window.ttsPlaybackSpeed = 1.2;
} else if (navigator.userAgent.includes("Windows")) {
window.ttsPlaybackSpeed = 5;
} else {
window.ttsPlaybackSpeed = 1;
}
/* Text to Speech function. Adjust the values of `ttsPlaybackSpeed` increase/decrease the playback speed. */
function runTTS(tries = 0) {
const text = window.getSelection().toString().trim();
/* Stop any currently playing TTS and empty the queue */
if (speechSynthesis.speaking) {
return speechSynthesis.cancel();
}
/* Only continue if the current selection contains text */
if (!text) {
return;
}
/* Start speaking the currently selected text at the user-define speed*/
const msg = new SpeechSynthesisUtterance();
msg.rate = window.ttsPlaybackSpeed || 1;
msg.text = text;
if (navigator.userAgent.includes("Macintosh")) {
/* Chrome defaults to using "Samantha" but Firefox does not */
for (const voice of speechSynthesis.getVoices()) {
if (voice.name == "Samantha") {
msg.voice = voice;
break;
}
}
/* Sometimes the full voice list won't load the first time it's retrieved (observed in Firefox on macOS) */
if (msg.voice?.name !== "Samantha") {
if (tries < MAX_TRIES) {
setTimeout(() => runTTS(++tries), TRY_DELAY);
return;
} else {
console.log(`Voice did not load within ${MAX_TRIES * TRY_DELAY} ms.`);
return;
}
} else {
if (tries > 0) {
console.log(`Took ${tries} tries (${tries * TRY_DELAY} ms) to load desired voice.`)
}
}
}
speechSynthesis.speak(msg);
}
/* Speak the current selection when the user presses Alt+I (Mac: Option+I) or Alt+1 (Mac: Option+1) */
document.addEventListener("keydown", (e) => {
if (!e.metaKey && !e.shiftKey && !e.ctrlKey && !e.repeat) {
if (
(e.code === "Digit1" && e.altKey) ||
(e.code === "KeyI" && e.altKey)
) {
e.preventDefault();
e.stopPropagation();
window.runTTS();
}
}
}, { capture: true });
window.runTTS = runTTS;
}
/* Start speaking the current selection when the bookmarklet is clicked */
window.runTTS();
}
@rongjiecomputer
Copy link

You should add e.preventDefault() inside if block at line 16 so that Ctrl+S does not trigger "save page" in Chrome.

@dotproto
Copy link
Author

dotproto commented Jun 4, 2017

Good call, @rongjiecomputer. I've updated the script with that and a couple other tweaks.

@dotproto
Copy link
Author

dotproto commented Dec 9, 2024

  • Change key bindings from Ctrl+S (typically used to save a page) to Alt+I or Alt+1
  • Introduce per-OS default speech speed
  • Make speech speed controllable at runtime by modifying window.ttsPlaybackSpeed
  • Default to using the "Samantha" voice on macOS
  • Updated to improve behavior in Firefox

@dotproto
Copy link
Author

dotproto commented Jan 7, 2025

Changes in the latest revision

  • Updated keyboard shortcut logic to ignore other modifier keys. This fixes a bug where Cmd+Opt+I would not open devtools after the bookmarklet was triggered.
  • Adjusted timeouts to increase the likelihood that the desired voice is used in Firefox on macOS.
    • Also added additional logging to help investigate when the desired voice is not used.
  • Non-functional: moved initialization logic into the initial if check to see if runTTS is defined. This makes it so re-triggering the bookmarklet on a page where it has already been used only triggers the existing script to run again.

@ashtonmeuser
Copy link

ashtonmeuser commented Jan 8, 2025

Hi, @dotproto! I just stumbled upon this bookmarklet you've created and figured you may find my service bookmarkl.ink helpful. You can make your bookmarklet a little easier to share using https://bookmarkl.ink/dotproto/febf568f177dca9a7cf4ede329e53667. Further, you can add a title and description using the following.

// bookmarklet-title: Selection TTS
// bookmarklet-about: Read the selected text aloud using Text to Speech (TTS).

Lastly, you could make the bookmarklet configurable by adding a speed variable.

// bookmarklet-var(number): speed
const adjusted = 1 + Math.max(-1.0, Math.min(1.0, (speed ?? 0) / 10)); // It may be helpful to massage this number

PS: You can use TypeScript if that's your cup of tea.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment