Skip to content

Instantly share code, notes, and snippets.

@fuweichin
Last active October 23, 2022 07:52
Show Gist options
  • Save fuweichin/5362c080fd2a7debef51b22ff662e59b to your computer and use it in GitHub Desktop.
Save fuweichin/5362c080fd2a7debef51b22ff662e59b to your computer and use it in GitHub Desktop.
adaptive sync throttling for Canvas / WebGL applications
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Adaptive Sync Throttling</title>
<style>
input[type=range][orient=vertical] {
writing-mode: bt-lr;
-webkit-appearance: slider-vertical;
width: 8px;
height: 128px;
padding: 0 5px;
}
</style>
</head>
<body>
<h2>Comparison</h2>
<h3>Input without throttling</h3>
<div>
Color
<input type="color" id="color" value="#cc00ff" />
</div>
<h3>Input throttled</h3>
<div>
Wavelength (lambda):
<input type="range" id="lambda" value="400" min="380" max="780" />
</div>
<h3>Input with Adaptive Sync throttling</h3>
<div>
Transparency (alpha):
<input type="range" id="alpha" orient="vertical" min="0" max="255" />
</div>
<h2>Usage</h2>
<ul>
<li>drag range slider / color picker</li>
<li>open browser DevTools </li>
<li>see Console logs and compare average throttle effect</li>
</ul>
<script type="module">
import {throttle} from 'https://unpkg.com/throttle-debounce@5.0.0/esm/index.js';
import {throttleAdaptiveSync} from './throttle.js';
const $ = (s, c = document) => c.querySelector(s);
const $$ = (s, c = document) => Array.prototype.slice.call(c.querySelectorAll(s));
function main() {
console.time('t1');
$('#color').addEventListener('input', (e) => {
console.timeEnd('t1');
console.time('t1');
});
console.time('t2');
$('#lambda').addEventListener('input', throttle(16, (e) => {
console.timeEnd('t2');
console.time('t2');
}));
console.time('t3');
$('#alpha').addEventListener('input', throttleAdaptiveSync((e) => {
console.timeEnd('t3');
console.time('t3');
}));
}
main();
</script>
</body>
</html>
/**
* throttle based on requestAnimationFrame
* @param {Function} func
* @returns {Function}
*/
export function throttleAdaptiveSync(func) {
let lastTriggerTime = 0;
let nextTimerHandle = 0;
let nextCallInfo = null;
let checkNextCall = () => {
let now = performance.now();
nextTimerHandle = 0;
if (nextCallInfo !== null) {
lastTriggerTime = now;
let callInfo = nextCallInfo;
nextCallInfo = null;
func.apply(callInfo[0], callInfo[1]);
}
};
let proxyFunc = function () {
let now = performance.now();
if (lastTriggerTime === 0) {
lastTriggerTime = now;
nextTimerHandle = requestAnimationFrame(checkNextCall);
nextCallInfo = null;
func.apply(this, arguments);
} else {
if (nextTimerHandle > 0) {
nextCallInfo = [this, arguments];
} else {
nextCallInfo = [this, arguments];
nextTimerHandle = requestAnimationFrame(checkNextCall);
}
}
};
return proxyFunc;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment