I’m facing an issue where the new Read Aloud feature in the ChatGPT web version doesn’t work on Firefox (Ubuntu 22.04). The button appears active, but there’s no audio playback, unlike on Chrome or Brave where it works fine.
Is this a known bug? Has anyone else experienced it or found a workaround? Hoping for a fix or any tips from the community or OpenAI!
Update: This is the error, the Firefox console throws, whenever I try to play the audio: Uncaught (in promise) DOMException: MediaSource.addSourceBuffer: Type not supported in MediaSource NextJS 33 sourceopen e_ eS d h eU eH re re rn ro oN eF ro nU nD ru rr ra ra hydrateRoot ef ef ed ep 91262 promise callback*91262 p <anonymous> O <anonymous> u <anonymous>
I’m not technically skilled enough to find out, whether it’s a Firefox issue or a ChatGPT issue.
Hey everyone, the problem seems to be that Firefox can’t stream the AAC, but it can still play back “complete” AAC audio. There is a Userscript that captures the response from the Synthesize (i.e. Voice) API and opens it as an AAC blob in a new tab: Reddit - Dive into anything
It’s not a great solution, but it’s good enough if you just want to have it occasionally read something to you.
Note: The Userscript does not modify website behavior (even without it, the audio data is received), it just automates the process of opening dev tools and saving the response from the API as an .aac file and playing it back.
Thanks a lot for diving deeper into what the issue is and providing this workaround!
I implemented your Userscript via the Tampermonkey extension and it works!! Yes, it’s kind of a janky solution, but thanks to you, I don’t have to use Brave Browser for ChatGPT anymore.
I tried creating a browser extension, that converts the aac and then plays it back in the background, just like it’s supposed to, but I failed unfortunately. The idea was to use ffmpeg.wasm.
I modified the Userscript of @Winkelmann and now, the audio is being played directly on the website (no tab opening) and on top of the screen, there are controls to pause, stop, rewind and play from the beginning.
It’s not a very aesthetically pleasing solution and the audio takes longer to play, since it doesn’t stream and has to finish generating, but hey, now you can rewind the audio, which is not something the Read Aloud feature normally offers.
Here’s the Userscript code, that you need to enter into the Tampermonkey extension:
// ==UserScript==
// @name OpenAI Chat Synthesize Interceptor with Advanced Icon Controls
// @version 0.6
// @description Capture ChatGPT Synthesize API responses and add advanced playback controls with icons.
// @author You
// @match https://chat.openai.com/*
// @grant none
// ==/UserScript==
(function () {
// 'use strict';
let audio = null; // Global audio element
let playPauseBtn; // Global play/pause button
const originalFetch = window.fetch;
window.fetch = async function (url, options) {
const response = await originalFetch.apply(this, arguments);
if (url.startsWith('https://chat.openai.com/backend-api/synthesize')) {
console.log('Intercepted Synthesize API request for URL:', url);
const clone = response.clone();
const arrayBuffer = await clone.arrayBuffer();
const aacFile = new Blob([arrayBuffer], { type: 'audio/aac' });
const fileUrl = URL.createObjectURL(aacFile);
if (audio) {
audio.src = fileUrl;
} else {
audio = new Audio(fileUrl);
createControls();
}
audio.play().catch(err => console.error('Audio playback error:', err))
.then(() => {
playPauseBtn.innerHTML = '⏸'; // Change to Pause icon
showControlsTemporarily(); // Show controls when audio starts playing
});
}
return response;
};
function createControls() {
const controlsDiv = document.createElement('div');
controlsDiv.id = 'audioControls';
controlsDiv.style.position = 'fixed';
controlsDiv.style.top = '15px';
controlsDiv.style.left = '50%';
controlsDiv.style.transform = 'translateX(-50%)';
controlsDiv.style.zIndex = '10000';
controlsDiv.style.display = 'flex';
controlsDiv.style.gap = '10px';
controlsDiv.style.opacity = '0'; // Initially invisible, updated to be controlled by showControlsTemporarily
controlsDiv.style.transition = 'opacity 0.5s ease'; // Smooth transition for opacity
// Make controls visible on hover
controlsDiv.onmouseover = function() {
controlsDiv.style.opacity = '1';
};
// Make controls invisible again when not hovered
controlsDiv.onmouseout = function() {
controlsDiv.style.opacity = '0';
};
// Play from the beginning button
const startOverBtn = document.createElement('button');
startOverBtn.innerHTML = '⏮'; // Start over icon
startOverBtn.style.cursor = 'pointer';
startOverBtn.style.background = 'none';
startOverBtn.style.border = 'none';
startOverBtn.onclick = function() {
audio.currentTime = 0;
audio.play();
playPauseBtn.innerHTML = '⏸'; // Change to Pause icon
};
// Play/Pause button
playPauseBtn = document.createElement('button');
playPauseBtn.innerHTML = '▶️'; // Play icon, assuming audio is stopped at first
playPauseBtn.style.cursor = 'pointer';
playPauseBtn.style.background = 'none';
playPauseBtn.style.border = 'none';
playPauseBtn.onclick = function() {
if (audio.paused || audio.ended) {
audio.play();
playPauseBtn.innerHTML = '⏸'; // Change to Pause icon
} else {
audio.pause();
playPauseBtn.innerHTML = '▶️'; // Change to Play icon
}
};
// Rewind 10 seconds button
const rewindBtn = document.createElement('button');
rewindBtn.innerHTML = '⏪'; // Rewind icon
rewindBtn.style.cursor = 'pointer';
rewindBtn.style.background = 'none';
rewindBtn.style.border = 'none';
rewindBtn.onclick = function() {
audio.currentTime = Math.max(0, audio.currentTime - 10); // Rewind 10 seconds or to the start
if (audio.paused) {
audio.play();
playPauseBtn.innerHTML = '⏸'; // Ensure play/pause button shows Pause icon
}
};
// Stop button
const stopBtn = document.createElement('button');
stopBtn.innerHTML = '⏹'; // Stop icon
stopBtn.style.cursor = 'pointer';
stopBtn.style.background = 'none';
stopBtn.style.border = 'none';
stopBtn.onclick = function() {
audio.pause();
audio.currentTime = 0;
playPauseBtn.innerHTML = '▶️'; // Change to Play icon
};
controlsDiv.appendChild(startOverBtn);
controlsDiv.appendChild(rewindBtn);
controlsDiv.appendChild(playPauseBtn);
controlsDiv.appendChild(stopBtn);
document.body.appendChild(controlsDiv);
// Call this to initially show controls
showControlsTemporarily();
}
function showControlsTemporarily() {
const controlsDiv = document.getElementById('audioControls');
if (controlsDiv) {
controlsDiv.style.opacity = '1'; // Make controls visible
// Use setTimeout to fade controls out after 5 seconds
setTimeout(() => {
if (!controlsDiv.matches(':hover')) { // Only fade out if not hovering over controls
controlsDiv.style.opacity = '0';
}
}, 2000); // Adjust time as needed
}
}
})();
I can confirm this.
Not sure if they fixed it with Firefox 124 or if OpenAI changed something on their side, but now Read Aloud works without that Tampermonkey script for me.
Hi, could you tell me how to use this feature? I’m not very computer savvy, and it’s not working for me either. Could you please guide me on how to go to the settings and activate this feature on my computer or application? Thank you for your help
Just noting that I was having playback issues on both Firefox and Chrome. This solution is a decent fix, working as of 05/07/2024. Although 4things to note:
you will need to update the code to match chatgpt’s new url
It takes a fair bit longer to play the audio
when the audio does play, both the tamper monkey audio player and chatgpt audio player play over each other, so the user needs to click stop on the chatgpt player.
The tampermonkey audio player hidden top centre of screen. You have to hover your mouse there once the audio starts playing to use controls.
Here is the updated code for the tampermonkey extension with the current chatgpt urls:
EDIT: I have also added in some error handling, removed some redundant code and resolved some async issues.
// ==UserScript==
// @name OpenAI Chat Synthesize Interceptor with Advanced Icon Controls
// @version 0.7
// @description Capture ChatGPT Synthesize API responses and add advanced playback controls with icons.
// @author You
// @match https://chatgpt.com/*
// @grant none
// ==/UserScript==
(function () {
'use strict';
let audio = null; // Global audio element
let playPauseBtn; // Global play/pause button
let controlsDiv; // Global controls div
const originalFetch = window.fetch;
window.fetch = async function (url, options) {
try {
const response = await originalFetch.apply(this, arguments);
if (url.startsWith('https://chatgpt.com/backend-api/synthesize')) {
console.log('Intercepted Synthesize API request for URL:', url);
const clone = response.clone();
const arrayBuffer = await clone.arrayBuffer();
const aacFile = new Blob([arrayBuffer], { type: 'audio/aac' });
const fileUrl = URL.createObjectURL(aacFile);
if (!audio) {
audio = new Audio(fileUrl);
createControls();
} else {
audio.src = fileUrl;
}
audio.play().then(() => {
playPauseBtn.innerHTML = '⏸'; // Change to Pause icon
showControlsTemporarily(); // Show controls when audio starts playing
}).catch(err => console.error('Audio playback error:', err));
}
return response;
} catch (error) {
console.error('Fetch interception error:', error);
throw error;
}
};
function createControls() {
if (controlsDiv) return; // Avoid creating controls multiple times
controlsDiv = document.createElement('div');
controlsDiv.id = 'audioControls';
controlsDiv.style.position = 'fixed';
controlsDiv.style.top = '15px';
controlsDiv.style.left = '50%';
controlsDiv.style.transform = 'translateX(-50%)';
controlsDiv.style.zIndex = '10000';
controlsDiv.style.display = 'flex';
controlsDiv.style.gap = '10px';
controlsDiv.style.opacity = '0'; // Initially invisible
controlsDiv.style.transition = 'opacity 0.5s ease'; // Smooth transition for opacity
controlsDiv.onmouseover = () => controlsDiv.style.opacity = '1';
controlsDiv.onmouseout = () => controlsDiv.style.opacity = '0';
const startOverBtn = createButton('⏮', () => {
audio.currentTime = 0;
audio.play();
playPauseBtn.innerHTML = '⏸'; // Change to Pause icon
});
playPauseBtn = createButton('▶️', () => {
if (audio.paused || audio.ended) {
audio.play();
playPauseBtn.innerHTML = '⏸'; // Change to Pause icon
} else {
audio.pause();
playPauseBtn.innerHTML = '▶️'; // Change to Play icon
}
});
const rewindBtn = createButton('⏪', () => {
audio.currentTime = Math.max(0, audio.currentTime - 10); // Rewind 10 seconds or to the start
if (audio.paused) {
audio.play();
playPauseBtn.innerHTML = '⏸'; // Ensure play/pause button shows Pause icon
}
});
const stopBtn = createButton('⏹', () => {
audio.pause();
audio.currentTime = 0;
playPauseBtn.innerHTML = '▶️'; // Change to Play icon
});
controlsDiv.appendChild(startOverBtn);
controlsDiv.appendChild(rewindBtn);
controlsDiv.appendChild(playPauseBtn);
controlsDiv.appendChild(stopBtn);
document.body.appendChild(controlsDiv);
showControlsTemporarily();
}
function createButton(icon, onClick) {
const btn = document.createElement('button');
btn.innerHTML = icon;
btn.style.cursor = 'pointer';
btn.style.background = 'none';
btn.style.border = 'none';
btn.onclick = onClick;
return btn;
}
function showControlsTemporarily() {
if (controlsDiv) {
controlsDiv.style.opacity = '1'; // Make controls visible
setTimeout(() => {
if (!controlsDiv.matches(':hover')) { // Only fade out if not hovering over controls
controlsDiv.style.opacity = '0';
}
}, 2000); // Adjust time as needed
}
}
})();
same here its verry annoying. cant they have a feature to download all audios so i can listen on them with vlc player? i mean i’m paying 25 usd every month and it fails to read out message all the time. i think im switching to a more opensource or google gemeni.