How To Optimize Loading WebGL Scenes
If you don’t want 3D animations to tank your page speed, here is a quick optimization snippet.
Problem
If you’ve ever played with WebGL in your designs, you know that generating or importing 3D models can easily tank your google page speed score.
The reason is straightforward: the main thread spends too much time parsing the Javascript and slows the rendering of the rest of the page.
Solution
Put all the rendering logic in a web worker:
/public/workers/scene.js
import * as THREE from "https://unpkg.com/three@0.165.0/build/three.module.js";
let renderer, camera, scene;
self.onmessage = (e) => {
const data = e.data;
if (data.canvas) {
const canvas = data.canvas;
const width = data.width;
const height = data.height;
scene = new THREE.Scene();
// ... scene setup
function animate() {
self.requestAnimationFrame(animate);
// ...animation logic
renderer.render(scene, camera);
}
animate();
}
if (data.type === "resize" && renderer && camera) {
renderer.setSize(data.width, data.height, false);
camera.aspect = data.width / data.height;
camera.updateProjectionMatrix();
}
};
In your main thread, specify you want to use an offscreen canvas to render your 3D animation:
<canvas id="mainCanvas" class="fixed top-0 left-0 w-full h-full z-[-1]"
></canvas>
<script>
const worker = new Worker("/workers/scene.js", { type: "module" });
const canvas = document.getElementById("mainCanvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const offscreen = canvas.transferControlToOffscreen();
worker.postMessage(
{
canvas: offscreen,
width: window.innerWidth,
height: window.innerHeight,
},
[offscreen],
);
window.addEventListener("resize", () => {
const width = window.innerWidth;
const height = window.innerHeight;
worker.postMessage({ type: "resize", width, height });
});
</script>
We use one message type for setting up the scene, and another to handle window resizing.
The 3D scene will be rendered in a web worker, separate from the main thread so as to not block it.
See it for yourself: simply copy/paste the URL of this article in google page speed or run lighthouse directly from the chrome devtools, and you’ll see the performance is unaffected by the worker script I used to load the 3D background animation.