Created
March 29, 2025 16:45
-
-
Save steveseguin/ffdb57b7435fcec797fc89e9299294ba to your computer and use it in GitHub Desktop.
Browsers are annoying these days. But solutions exist
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function createReliableTimer(callback, delay, data) { | |
// Check if we're in Electron or OBS Studio | |
if (window.electronApi || window.obsstudio) { | |
// Use standard setTimeout for these environments | |
const timerId = setTimeout(callback, delay, data); | |
return { | |
clear: function() { | |
clearTimeout(timerId); | |
} | |
}; | |
} | |
// For browser environments, try to use AudioContext | |
let fallbackTimerId = null; | |
// Try to get an audioContext - might be suspended initially | |
const audioContext = new (window.AudioContext || window.webkitAudioContext)(); | |
// Create a resume function we can call if needed | |
const tryResumeAudio = async function() { | |
if (audioContext.state === "suspended") { | |
try { | |
await audioContext.resume(); | |
return audioContext.state === "running"; | |
} catch (e) { | |
console.warn("Could not resume AudioContext", e); | |
return false; | |
} | |
} | |
return audioContext.state === "running"; | |
}; | |
// Create a wrapper function to pass the data | |
const wrappedCallback = function() { | |
callback(data); | |
}; | |
// Set up a fallback setTimeout as backup | |
fallbackTimerId = setTimeout(wrappedCallback, delay); | |
// Try to set up audio timer if possible | |
let audioComponents = null; | |
tryResumeAudio().then(audioRunning => { | |
if (audioRunning) { | |
const startTime = audioContext.currentTime; | |
const oscillator = audioContext.createOscillator(); | |
oscillator.frequency.value = 0; // Silent | |
const processor = audioContext.createScriptProcessor(4096, 1, 1); | |
processor.onaudioprocess = function() { | |
const elapsedTime = (audioContext.currentTime - startTime) * 1000; | |
if (elapsedTime >= delay) { | |
// Clear the fallback since we're handling it with audio | |
if (fallbackTimerId) { | |
clearTimeout(fallbackTimerId); | |
fallbackTimerId = null; | |
} | |
oscillator.stop(); | |
processor.disconnect(); | |
oscillator.disconnect(); | |
wrappedCallback(); | |
} | |
}; | |
oscillator.connect(processor); | |
processor.connect(audioContext.destination); | |
oscillator.start(); | |
audioComponents = { | |
oscillator, | |
processor | |
}; | |
} | |
}); | |
return { | |
clear: function() { | |
// Clear the fallback timer | |
if (fallbackTimerId) { | |
clearTimeout(fallbackTimerId); | |
fallbackTimerId = null; | |
} | |
// Clean up audio components if they were created | |
if (audioComponents) { | |
audioComponents.oscillator.stop(); | |
audioComponents.processor.disconnect(); | |
audioComponents.oscillator.disconnect(); | |
} | |
} | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment