Skip to content

Instantly share code, notes, and snippets.

@anildigital
Created August 9, 2026 16:47
Show Gist options
  • Select an option

  • Save anildigital/9a195654a687524fb544db62894466fe to your computer and use it in GitHub Desktop.

Select an option

Save anildigital/9a195654a687524fb544db62894466fe to your computer and use it in GitHub Desktop.
Repro: audio: 'loopbackWithMute' via custom setDisplayMediaRequestHandler never captures data on macOS
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>loopbackWithMute repro</title>
</head>
<body>
<h3>audio: 'loopbackWithMute' via custom setDisplayMediaRequestHandler picker</h3>
<pre id="log" style="white-space: pre-wrap; font-family: monospace;"></pre>
<script src="renderer.js"></script>
</body>
</html>
const { app, BrowserWindow, session, desktopCapturer } = require('electron')
function createWindow () {
const win = new BrowserWindow({
width: 900,
height: 600,
webPreferences: {
preload: __dirname + '/preload.js'
}
})
win.loadFile('index.html')
}
app.whenReady().then(() => {
// Custom picker path: we resolve the source ourselves (simulating an
// app's own branded picker UI) instead of setting `useSystemPicker: true`.
session.defaultSession.setDisplayMediaRequestHandler((request, callback) => {
desktopCapturer.getSources({ types: ['screen'] }).then((sources) => {
callback({ video: sources[0], audio: 'loopbackWithMute' })
})
}, { useSystemPicker: false })
createWindow()
})
// No preload APIs needed for this repro.
const log = (msg) => {
const el = document.getElementById('log')
el.textContent += msg + '\n'
console.log(msg)
}
navigator.mediaDevices.getDisplayMedia({ video: true, audio: true }).then((stream) => {
log('getDisplayMedia() resolved')
stream.getTracks().forEach((track) => {
log(`track: kind=${track.kind} label="${track.label}" readyState=${track.readyState}`)
track.addEventListener('ended', () => log(`EVENT: ${track.kind} track ended (label="${track.label}")`))
})
const audioTrack = stream.getAudioTracks()[0]
if (!audioTrack) {
log('No audio track was returned at all.')
return
}
// Web Audio analyser: proves whether any samples are actually flowing,
// independent of readyState/muted (which can lag or misreport).
const ctx = new AudioContext()
const source = ctx.createMediaStreamSource(new MediaStream([audioTrack]))
const analyser = ctx.createAnalyser()
source.connect(analyser)
const buf = new Float32Array(analyser.fftSize)
setInterval(() => {
analyser.getFloatTimeDomainData(buf)
let maxAbs = 0
for (let i = 0; i < buf.length; i++) maxAbs = Math.max(maxAbs, Math.abs(buf[i]))
log(
`t+${Math.round(performance.now() / 1000)}s: ` +
`readyState=${audioTrack.readyState} muted=${audioTrack.muted} peakAmplitude=${maxAbs.toFixed(4)}`
)
}, 1000)
}).catch((err) => {
log('getDisplayMedia() rejected: ' + err)
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment