How to create text convert to audio using HTML, CSS and JavaScript.
Posted on 2024-09-19
Hello, everyone! 👋 Today, we are going to learn how to create text convert to audio using HTML, CSS, and JavaScript. What will you gain from following this tutorial? I hope you will enhance your CSS styling skills and improve your JavaScript logic.
read also How to create a simpe calculator using HTML CSS and JavaScript
Firstly, we need to create a folder. You can name it "text-to-audio" or anything you prefer. Then, we will create HTML, CSS and JavaScript files within that folder. Name the HTML file "index.html", CSS file "style.css" and the JavaScript file "script.js".
In the HTML file, we will set the title within the 'head' tags. Name the title "text-to-audio". Then, you can following the code below.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Document</title>
</head>
<body>
<h1 class="h1">TextToAudio.dev</h1>
<p>Enter your script in the input below and press return to be audio.</p>
<p class="sub-p">Change voices using the dropdown menu</p>
<div class="main">
<form class="form">
<input type="text" class="txt" placeholder="type here..."/>
<div>
<label for="rate">Rate</label>
<input type="range" min="0.5" max="2" value="1" step="0.1" id="rate" />
<div class="rate-value">1</div>
<div class="clearfix"></div>
</div>
<div>
<label for="pitch">Pitch</label>
<input type="range" min="0" max="2" value="1" step="0.1" id="pitch">
<div class="pitch-value">1</div>
<div class="clearfix"></div>
</div>
<select></select>
<div class="controls">
<button id="play" type="submit" class="btn">Play</button>
</div>
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/mediarecorder-to-mp3@latest/dist/index.min.js"></script>
<script src="script.js"></script>
</body>
</html>
The code displayed above serves as an example. You can follow it as a guide.
Then, we need to add styling to our project to make it look more polished. You can follow the code below.
body,
html {
height: 100%;
margin: 0;
padding: 0;
}
html {
height: 100%;
}
body {
display: flexbox;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #58e9a5;
padding: 20px;
}
h1,
p {
font-family: Verdana, Geneva, Tahoma, sans-serif;
text-align: center;
padding: 20px;
font-size: 1.5rem;
}
.sub-p {
font-size: small;
font-weight: bold;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.main {
width: 50%;
max-width: 800px;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 20px;
border: solid 3px #000000;
border-radius: 8px;
background-color: #f4eee5;
box-shadow: 5px 5px 3px 5px #000000;
margin: 0 auto;
}
.h1 {
border: solid 3px #ffff;
font-weight: bold;
border-radius: 5px;
margin-left: 10%;
margin-right: 10%;
color: #ffff;
box-shadow: 3px 6px #ffff;
text-shadow: 2px 2px #000000;
}
.h1:hover {
box-shadow: 1px 1px #ffff;
margin-top: 20px;
}
.txt,
select,
form > div {
display: block;
margin: 0 auto;
font-family: sans-serif;
font-size: 16px;
padding: 5px;
}
.form {
margin: 15px;
}
.txt {
width: 80%;
padding: 1.25rem;
}
select {
width: 83%;
border:3px solid #000000;
border-radius: 5px;
padding: 0.75rem;
}
form > div {
width: 81%;
}
.txt,
form > div {
margin-bottom: 10px;
overflow: auto;
}
.clearfix {
clear: both;
}
label {
float: left;
width: 10%;
line-height: 1.5;
}
.rate-value,
.pitch-value {
float: right;
width: 5%;
line-height: 1.5;
}
#rate,
#pitch {
float: right;
width: 81%;
}
input {
border:3px solid #000000;
border-radius: 5px;
}
.controls {
text-align: center;
margin-top: 10px;
}
.download-link {
justify-content: center;
justify-items: center;
align-items: center;
}
a {
color: #3498db;
text-decoration: none;
font-weight: bold;
padding: 10px 15px;
border-radius: 5px;
transition: background-color 0.3s;
margin-bottom: 10px;
}
a.download-link {
display: inline-block;
background-color: #3498db;
color: #fff;
text-align: center;
font-size: 16px;
}
a.download-link:hover {
background-color: #2980b9;
}
.controls {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 10px;
}
.controls button {
padding: 10px;
background-color: #FFFF00;
border: solid 3px #000000;
border-radius: 3px;
font-weight: bold;
margin-bottom: 10px;
box-shadow: 3px 3px 6px #000000;
}
.controls button:hover {
background-color: #8f8f0f;
box-shadow: 1px 3px 1px #000000;
}
.download-link-container {
position: relative;
}
.close-button {
position: absolute;
top: 5px;
right: 5px;
background: #ff0000;
color: #ffffff;
border: none;
border-radius: 50%;
width: 25px;
height: 25px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.close-button:hover {
background: #cc0000;
}
Based on the code above, it is not responsive yet. So, we will make it responsive. Here’s the CSS code.
@media (max-width: 768px) {
body {
height: auto;
max-width: 100%;
margin: 0;
padding: 10px;
}
.main {
width: 90%;
padding: 10px;
}
.h1 {
margin-left: 5%;
margin-right: 5%;
font-size: 1.5em;
}
}
Next, We will add the function logic in JavaScript. To make the text convert to audio, we will using speechSynthesis API. speechSynthesis API allows you to convert text into spoken words, opening up a myriad of possibilities for interactive and engaging web applications. You can follow the code below. I have expleined in the comment.
const synth = window.speechSynthesis;
const inputForm = document.querySelector("form");
const inputTxt = document.querySelector(".txt");
const voiceSelect = document.querySelector("select");
const pitch = document.querySelector("#pitch");
const pitchValue = document.querySelector(".pitch-value");
const rate = document.querySelector("#rate");
const rateValue = document.querySelector(".rate-value");
let voices = [];
let audioChunks = []; // to store chunks audio
// Inisialisation AudioContext & MediaStreamDestination to record
const audioContext = new AudioContext();
const destination = audioContext.createMediaStreamDestination();
const mediaRecorder = new MediaRecorder(destination.stream);
// Populate list voices
function populateVoiceList() {
voices = synth.getVoices().sort(function (a, b) {
const aname = a.name.toUpperCase();
const bname = b.name.toUpperCase();
if (aname < bname) {
return -1;
} else if (aname === bname) {
return 0;
} else {
return +1;
}
});
const selectedIndex =
voiceSelect.selectedIndex < 0 ? 0 : voiceSelect.selectedIndex;
voiceSelect.innerHTML = "";
for (let i = 0; i < voices.length; i++) {
const option = document.createElement("option");
option.textContent = `${voices[i].name} (${voices[i].lang})`;
if (voices[i].default) {
option.textContent += " -- DEFAULT";
}
option.setAttribute("data-lang", voices[i].lang);
option.setAttribute("data-name", voices[i].name);
voiceSelect.appendChild(option);
}
voiceSelect.selectedIndex = selectedIndex;
}
populateVoiceList();
if (speechSynthesis.onvoiceschanged !== undefined) {
speechSynthesis.onvoiceschanged = populateVoiceList;
}
function speak() {
if (synth.speaking) {
console.error("speechSynthesis.speaking");
return;
}
if (inputTxt.value !== "") {
const utterThis = new SpeechSynthesisUtterance(inputTxt.value);
utterThis.onend = function (event) {
console.log("SpeechSynthesisUtterance.onend");
mediaRecorder.stop(); // Stop record while don't speak
};
utterThis.onerror = function (event) {
console.error("SpeechSynthesisUtterance.onerror");
};
const selectedOption =
voiceSelect.selectedOptions[0].getAttribute("data-name");
for (let i = 0; i < voices.length; i++) {
if (voices[i].name === selectedOption) {
utterThis.voice = voices[i];
break;
}
}
utterThis.pitch = pitch.value;
utterThis.rate = rate.value;
// connect audio from SpeechSynthesis to AudioContext
const audioElement = new Audio();
audioElement.srcObject = destination.stream;
const source = audioContext.createMediaElementSource(audioElement);
source.connect(destination);
source.connect(audioContext.destination);
// Start recording while speak
mediaRecorder.start();
// starting speak
synth.speak(utterThis);
}
}
// Event handler recording
mediaRecorder.ondataavailable = function (event) {
audioChunks.push(event.data);
};
mediaRecorder.onstop = function () {
const audioBlob = new Blob(audioChunks, { type: 'audio/wav' });
const audioURL = URL.createObjectURL(audioBlob);
// display audi recording
const audio = new Audio(audioURL);
audio.play();
// Close button
const closeButton = document.createElement('button');
closeButton.textContent = 'X'; // Teks tombol tutup
closeButton.classList.add('close-button');
// add eventListener to close button
closeButton.addEventListener('click', (event) => {
event.stopPropagation(); // preven close button click
event.preventDefault(); // preven default action button if there.
if (downloadLinkContainer.parentElement) {
downloadLinkContainer.remove(); // remove all the container
}
});
// create element <a>
const downloadLink = document.createElement('a');
downloadLink.href = audioURL;
downloadLink.download = 'speech_recording.wav';
downloadLink.classList.add('download-link');
downloadLink.textContent = 'Download Recording';
// wrap link download & close button with container
const downloadLinkContainer = document.createElement('div');
downloadLinkContainer.classList.add('download-link-container');
downloadLinkContainer.appendChild(downloadLink); // add link download to container
downloadLinkContainer.appendChild(closeButton); // add close button to container
// find container with controls class
const controlsContainer = document.querySelector('.controls');
// add container
if (controlsContainer) {
controlsContainer.appendChild(downloadLinkContainer);
}
};
inputForm.onsubmit = function (event) {
event.preventDefault();
audioChunks = []; // Reset array audioChunks
speak();
inputTxt.blur();
};
pitch.onchange = function () {
pitchValue.textContent = pitch.value;
};
rate.onchange = function () {
rateValue.textContent = rate.value;
};
voiceSelect.onchange = function () {
speak();
};
Next, you can run your code from the terminal.
The result will look like this
Thank you for reading! If you found this post helpful, please spread the word.
We'd love to hear from you! Let us know how we can better assist you by sharing your suggestions.
Contact us: https://muhamadalzika.com/about Email: zikafakk@gmail.com