I am trying to use my PlayHT API in order to add voice to my character. I am using the custom javascript section in advanced options in order to achieve this. I have a very rudimentary understanding of code so that combined with a bit of ChatGPT for help, I ended up with this:
async function getCustomPlayHTVoices() { const apiKey = “Removed for security”; // Your PlayHT API key const userId = “Removed for security”; // Your PlayHT User ID
try {
const response = await fetch(“https://api.play.ht/api/v2/cloned-voices”, {
headers: {
“Authorization”: Bearer ${apiKey}
,
“X-User-ID”: userId
}
});
if (!response.ok) throw new Error("Failed to fetch PlayHT voices");
const voices = await response.json();
const customVoices = voices.filter(v => v.name.toLowerCase().includes("arlecchino"));
if (customVoices.length === 0) console.warn("Custom voice 'Arlecchino' not found.");
return customVoices;
} catch (error) { console.error(error); return []; } }
// Populate voice selection dropdown (async () => { const playHTVoices = await getCustomPlayHTVoices(); if (playHTVoices.length === 0) { document.body.innerHTML = “<p>Error fetching PlayHT voices. Check your API key or voice availability.</p>”; return; }
const voiceOptions = playHTVoices
.map(v => <option value="${v.id}">${v.name}</option>
)
.join(“”);
document.body.innerHTML = <p>Please choose a voice:</p> <select id="voiceSelect">${voiceOptions}</select> <br> <button onclick="setVoice()">Submit</button>
;
window.chosenVoiceId = playHTVoices[0].id; // Default voice })();
function setVoice() { window.chosenVoiceId = document.getElementById(“voiceSelect”).value; oc.window.hide(); }
oc.window.show();
// Listen for streamed text and convert it to speech using PlayHT let sentence = “”; oc.thread.on(“StreamingMessage”, async function (data) { for await (let chunk of data.chunks) { sentence += chunk.text;
// Check for end of sentence OR limit to 200 characters to prevent buffering too much text
let endOfSentenceIndex = sentence.search(/[.!?]/);
if (endOfSentenceIndex !== -1 || sentence.length > 200) {
let sentenceToSpeak = sentence.slice(0, endOfSentenceIndex + 1 || sentence.length);
console.log("Speaking sentence:", sentenceToSpeak);
await textToSpeech({ text: sentenceToSpeak, voiceId: window.chosenVoiceId });
sentence = sentence.slice(endOfSentenceIndex + 1).trim(); // Reset for next sentence
}
} });
// Function to send text to PlayHT API and play the generated audio async function textToSpeech({ text, voiceId }) { const apiKey = “Removed for security”; // Your PlayHT API key const userId = “Removed for security”; // Your PlayHT User ID
try {
const response = await fetch(“https://api.play.ht/api/v2/tts/stream”, {
method: “POST”,
headers: {
“Authorization”: Bearer ${apiKey}
,
“X-User-ID”: userId,
“Content-Type”: “application/json”
},
body: JSON.stringify({
text: text,
voice: voiceId,
format: “mp3”
})
});
if (!response.ok) throw new Error("Failed to fetch audio from PlayHT");
const result = await response.json();
console.log("PlayHT Response:", result);
if (!result.audioUrl) throw new Error("No audio URL returned from PlayHT");
return new Promise(resolve => {
const audio = new Audio(result.audioUrl);
audio.onended = resolve;
audio.play();
});
} catch (error) { console.error(error); } }
I would also like to understand where I went wrong so please explain if you know the solution. Apologies as well, I can’t get the code to be in a single sheet in preview. I guess that tells me a lot about my ability to code.
Thank you for the help. One thing to note is
window.chosenVoiceName = playHTVoices[0].id;
I had to put it after the html body due to it not appearing if it was before, but otherwise this works perfectly. Thank you again.