Activating Text to Speech through Variable Control

Hello I made this code that when ctrl + alt + 8 is clicked it will activate the text to speech code but instead I want it to activate using the variable $Speech that is set to True, how can I do this?

<script>
document.addEventListener('keydown', function(event) {
    if (event.ctrlKey && event.altKey && event.key === '8') {
        let isReading = true;

        const readSelectedText = () => {
            const selectedText = window.getSelection().toString();
            if (selectedText && isReading) {
                const utterance = new SpeechSynthesisUtterance(selectedText);
                utterance.voice = speechSynthesis.getVoices().find(voice => voice.name === 'Google UK English Male');
                utterance.rate = 1; // Adjust the rate as needed
                speechSynthesis.speak(utterance);
            }
        };

        const stopReading = () => {
            isReading = false;
            speechSynthesis.cancel();
        };

        readSelectedText();
        document.addEventListener('mouseup', readSelectedText);
        document.addEventListener('keydown', function(event) {
            if (event.key === 'Escape') {
                stopReading();
            }
        });
    }
});
</script>

warning: Harlowe has been deliberately designed to limit an Author’s ability to use JavaScript to extend the functionality of their project or of the Harlowe runtime engine itself. So JavaScript that runs correctly in the current releases of Harlowe may not do so in future releases.

Harlowe doesn’t include the ability to monitor for a variable value change, so there isn’t a way to automatically trigger something when the value of a variable named $Speech changes to true.

But as you (the Author) are in control of when that variable change happens in your project, you can also react to that change as needed.

1: Changing your Event based code to a set of callable functions.

note: the following JavaScript uses a unique Namespace to isolate the defined functions from any either that already exist on the web-browser’s window interface or that may be added in the future by the web-browser developer, or by an JavaScript addon. I am using a unique name of GE (short for Greyelf) in the example, I strongly suggest you use a different name for the Namespace in your own project.

The following code should be added to the project’s Story Javascript area

/* Create a unique Namespace to store methods & properties on */
window.GE = window.GE || {};

GE.readSelectedText = () => {
	console.log('readSelectedText');
	const selectedText = window.getSelection().toString();
	if (selectedText) {
		console.log('readSelectedText: has selection');
		const utterance = new SpeechSynthesisUtterance(selectedText);
		utterance.voice = speechSynthesis.getVoices().find(voice => voice.name === 'Google UK English Male');
		utterance.rate = 1; // Adjust the rate as needed
      
		$(document).on('keydown.reading', (event) => {
			if (event.key === 'Escape') {
				GE.stopReading();
			}
        });
		speechSynthesis.speak(utterance);
	}
};

GE.stopReading = () => {
	speechSynthesis.cancel();
	$(document).off('keydown.reading');
};

I also added the following function definition so I could programmatically select the textual contents of the Named Hook, which I will used in my test code. You may not need this function in your own project.

GE.selectHook = (name) => {
	let hook = 'tw-hook[name="' + name + '"]';
	if (document.selection) { // IE
		let range = document.body.createTextRange();
		range.moveToElementText(document.querySelector(hook));
		range.select();
    } else if (window.getSelection) {
        let range = document.createRange();
        range.selectNode(document.querySelector(hook));
        window.getSelection().removeAllRanges();
        window.getSelection().addRange(range);
    }
};

2: Using the functions on the Namespace as needed.

I tested the above using Passage code like the following…

|speak>[Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.]

(link-repeat: "Set Speech to true")[
	(set: $Speech to true)
	<script>
		GE.selectHook('speak');
		GE.readSelectedText();
	</script>
]

(link-repeat: "Set Speech to false")[
	(set: $Speech to false)
	<script>GE.stopReading()</script>
]

note: obviously you will use different code to change the value of the variable, but the point of the above was to show how to use a <script> element to call the relevant GE.readSelectedText() and GE.stopReading() functions when needed.

1 Like