shibly100's picture
Update script.js
f56b852 verified
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
if (!SpeechRecognition) {
document.getElementById("statusMessage").innerText =
"❌ This browser does NOT support speech recognition. Use Chrome Desktop.";
throw new Error("SpeechRecognition unsupported");
}
let recognition = null;
let isRecording = false;
let lastFinalTimestamp = null;
let pauseTimer = null;
const languageSelect = document.getElementById("languageSelect");
const toggleButton = document.getElementById("toggleButton");
const transcriptionOutput = document.getElementById("transcriptionOutput");
const statusMessage = document.getElementById("statusMessage");
const clearButton = document.getElementById("clearButton");
const downloadButton = document.getElementById("downloadButton");
function highlightDisfluencies(text) {
return text.replace(
/\b(um+|uh+|er+|ah+|eh+|oh+|hmm+|mmm+|like)\b/gi,
(m) => `<span class="disfluency">${m}</span>`
);
}
function addPauseMarker() {
transcriptionOutput.innerHTML += " <span class='pause'>…</span> ";
transcriptionOutput.scrollTop = transcriptionOutput.scrollHeight;
}
function initRecognition(lang) {
recognition = new SpeechRecognition();
recognition.lang = lang;
recognition.continuous = true;
recognition.interimResults = true;
recognition.onstart = () => {
statusMessage.innerText = `🎙️ Listening… (${lang})`;
};
recognition.onerror = (e) => {
statusMessage.innerText = `⚠️ Error: ${e.error}`;
if (e.error === "not-allowed") {
statusMessage.innerText =
"❌ Microphone blocked. Click the 🔒 icon → Allow microphone.";
}
};
recognition.onend = () => {
if (isRecording) recognition.start();
};
recognition.onresult = (event) => {
let interimText = "";
let finalText = "";
for (let i = event.resultIndex; i < event.results.length; i++) {
const text = event.results[i][0].transcript.trim();
if (event.results[i].isFinal) {
finalText += text + " ";
} else {
interimText += text + " ";
}
}
// ---- INTERIM (live) ----
if (interimText) {
const base = transcriptionOutput.innerHTML.replace(/<span class="interim">.*?<\/span>/, "");
transcriptionOutput.innerHTML = base +
`<span class="interim">${highlightDisfluencies(interimText)}</span>`;
transcriptionOutput.scrollTop = transcriptionOutput.scrollHeight;
clearTimeout(pauseTimer);
pauseTimer = setTimeout(() => addPauseMarker(), 2000);
}
// ---- FINAL ----
if (finalText) {
let cleaned = transcriptionOutput.innerHTML.replace(/<span class="interim">.*?<\/span>/, "");
const now = Date.now();
if (lastFinalTimestamp && now - lastFinalTimestamp > 3000) {
cleaned += "<br><span class='pause'>…</span> ";
}
transcriptionOutput.innerHTML =
cleaned + highlightDisfluencies(finalText);
transcriptionOutput.scrollTop = transcriptionOutput.scrollHeight;
lastFinalTimestamp = now;
}
};
}
toggleButton.addEventListener("click", () => {
if (isRecording) {
isRecording = false;
recognition.stop();
toggleButton.textContent = "🎤 Start Listening";
statusMessage.innerText = "Stopped.";
} else {
initRecognition(languageSelect.value);
recognition.start();
isRecording = true;
toggleButton.textContent = "⏹ Stop Listening";
}
});
clearButton.addEventListener("click", () => {
transcriptionOutput.innerHTML = "";
statusMessage.innerText = "Cleared.";
});
downloadButton.addEventListener("click", () => {
const text = transcriptionOutput.innerText;
const blob = new Blob([text], { type: "text/plain" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "transcription.txt";
a.click();
});