Skip to content

Commit a53102f

Browse files
jabuwujakobhellermann
authored andcommitted
add AudioContext workaround
1 parent f0abcdc commit a53102f

File tree

2 files changed

+133
-0
lines changed

2 files changed

+133
-0
lines changed

static/index.html

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,69 @@
5959
e.preventDefault();
6060
e.stopPropagation();
6161
});
62+
63+
// Insert hack to make sound autoplay on Chrome as soon as the user interacts with the tab:
64+
// https://developers.google.com/web/updates/2018/11/web-audio-autoplay#moving-forward
65+
66+
// the following function keeps track of all AudioContexts and resumes them on the first user
67+
// interaction with the page. If the function is called and all contexts are already running,
68+
// it will remove itself from all event listeners.
69+
(function () {
70+
// An array of all contexts to resume on the page
71+
const audioContextList = [];
72+
73+
// An array of various user interaction events we should listen for
74+
const userInputEventNames = [
75+
"click",
76+
"contextmenu",
77+
"auxclick",
78+
"dblclick",
79+
"mousedown",
80+
"mouseup",
81+
"pointerup",
82+
"touchend",
83+
"keydown",
84+
"keyup",
85+
];
86+
87+
// A proxy object to intercept AudioContexts and
88+
// add them to the array for tracking and resuming later
89+
self.AudioContext = new Proxy(self.AudioContext, {
90+
construct(target, args) {
91+
const result = new target(...args);
92+
audioContextList.push(result);
93+
return result;
94+
},
95+
});
96+
97+
// To resume all AudioContexts being tracked
98+
function resumeAllContexts(_event) {
99+
let count = 0;
100+
101+
audioContextList.forEach((context) => {
102+
if (context.state !== "running") {
103+
context.resume();
104+
} else {
105+
count++;
106+
}
107+
});
108+
109+
// If all the AudioContexts have now resumed then we unbind all
110+
// the event listeners from the page to prevent unnecessary resume attempts
111+
// Checking count > 0 ensures that the user interaction happens AFTER the game started up
112+
if (count > 0 && count === audioContextList.length) {
113+
userInputEventNames.forEach((eventName) => {
114+
document.removeEventListener(eventName, resumeAllContexts);
115+
});
116+
}
117+
}
118+
119+
// We bind the resume function for each user interaction
120+
// event on the page
121+
userInputEventNames.forEach((eventName) => {
122+
document.addEventListener(eventName, resumeAllContexts);
123+
});
124+
})();
62125
</script>
63126
</body>
64127

static/index_no_module.html

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,76 @@
5757
e.stopPropagation();
5858
});
5959
</script>
60+
61+
<script>
62+
document.body.addEventListener("contextmenu", (e) => {
63+
e.preventDefault();
64+
e.stopPropagation();
65+
});
66+
67+
// Insert hack to make sound autoplay on Chrome as soon as the user interacts with the tab:
68+
// https://developers.google.com/web/updates/2018/11/web-audio-autoplay#moving-forward
69+
70+
// the following function keeps track of all AudioContexts and resumes them on the first user
71+
// interaction with the page. If the function is called and all contexts are already running,
72+
// it will remove itself from all event listeners.
73+
(function () {
74+
// An array of all contexts to resume on the page
75+
const audioContextList = [];
76+
77+
// An array of various user interaction events we should listen for
78+
const userInputEventNames = [
79+
"click",
80+
"contextmenu",
81+
"auxclick",
82+
"dblclick",
83+
"mousedown",
84+
"mouseup",
85+
"pointerup",
86+
"touchend",
87+
"keydown",
88+
"keyup",
89+
];
90+
91+
// A proxy object to intercept AudioContexts and
92+
// add them to the array for tracking and resuming later
93+
self.AudioContext = new Proxy(self.AudioContext, {
94+
construct(target, args) {
95+
const result = new target(...args);
96+
audioContextList.push(result);
97+
return result;
98+
},
99+
});
100+
101+
// To resume all AudioContexts being tracked
102+
function resumeAllContexts(_event) {
103+
let count = 0;
104+
105+
audioContextList.forEach((context) => {
106+
if (context.state !== "running") {
107+
context.resume();
108+
} else {
109+
count++;
110+
}
111+
});
112+
113+
// If all the AudioContexts have now resumed then we unbind all
114+
// the event listeners from the page to prevent unnecessary resume attempts
115+
// Checking count > 0 ensures that the user interaction happens AFTER the game started up
116+
if (count > 0 && count === audioContextList.length) {
117+
userInputEventNames.forEach((eventName) => {
118+
document.removeEventListener(eventName, resumeAllContexts);
119+
});
120+
}
121+
}
122+
123+
// We bind the resume function for each user interaction
124+
// event on the page
125+
userInputEventNames.forEach((eventName) => {
126+
document.addEventListener(eventName, resumeAllContexts);
127+
});
128+
})();
129+
</script>
60130
</body>
61131

62132
</html>

0 commit comments

Comments
 (0)