Three.js 30,815 (three@0.186.1, MIT) ships WebGPURenderer from the three/webgpu entry point. It draws on WebGPU where available and falls back to a WebGL 2 backend otherwise, with the same scene, camera and materials either way. Its materials are written in TSL, the Three.js Shading Language, JavaScript functions that compile to WGSL or GLSL depending on the backend; that is how one material serves both. Three.js builds BookNest's 3D bookshelf with Three.js and uses this renderer; the only visible difference from WebGLRenderer is that initialization is asynchronous (await renderer.init()), since requesting an adapter and device is.
<!doctype html>
<style>
body { margin: 0; background: #f7f4ee; font: 13px system-ui, sans-serif; }
.stage { position: relative; width: 100%; max-width: 600px; }
canvas { display: block; width: 100%; height: auto; }
#note { position: absolute; left: 10px; top: 8px; color: #222; background: rgba(247,244,238,0.85); padding: 2px 6px; border-radius: 4px; }
</style>
<div class="stage">
<canvas id="view" width="600" height="340"></canvas>
<div id="note">Starting WebGPURenderer...</div>
</div>
<script type="importmap">
{ "imports": { "three": "https://cdn.jsdelivr.net/npm/three@0.186.1/build/three.webgpu.js" } }
</script>
<script type="module">
import * as THREE from 'three';
const canvas = document.getElementById('view');
const note = document.getElementById('note');
// WebGPURenderer uses WebGPU where it can and falls back to its WebGL 2 backend otherwise.
const renderer = new THREE.WebGPURenderer({ canvas, antialias: true });
renderer.setSize(600, 340, false);
await renderer.init(); // asynchronous: requesting an adapter and device is
const usingWebGPU = renderer.backend.isWebGPUBackend === true;
note.textContent = usingWebGPU ? 'WebGPURenderer is drawing with its WebGPU backend'
: ('gpu' in navigator ? 'No usable WebGPU adapter here: Three.js fell back to WebGL 2'
: 'WebGPU is not available in this browser: Three.js fell back to WebGL 2');
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xf7f4ee);
const camera = new THREE.PerspectiveCamera(40, 600 / 340, 0.1, 50);
camera.position.set(0, 1.6, 6);
camera.lookAt(0, 0.9, 0);
scene.add(new THREE.HemisphereLight(0xffffff, 0x886644, 1.2));
const sun = new THREE.DirectionalLight(0xffffff, 2);
sun.position.set(3, 5, 4);
scene.add(sun);
// A shelf of books: the same scene and materials work on either backend.
const shelf = new THREE.Group();
const wood = new THREE.MeshStandardMaterial({ color: 0x8b5a2b, roughness: 0.8 });
const plank = new THREE.Mesh(new THREE.BoxGeometry(4.4, 0.12, 1), wood);
shelf.add(plank);
const colors = [0x1f5f8b, 0x5c4099, 0xb5452e, 0xe0990f, 0x2d5d2a, 0x299e8f, 0x7a3b69, 0x2e6bc4];
colors.forEach((color, i) => {
const height = 1.1 + (i % 3) * 0.18;
const book = new THREE.Mesh(new THREE.BoxGeometry(0.34, height, 0.8),
new THREE.MeshStandardMaterial({ color, roughness: 0.55 }));
book.position.set(-1.9 + i * 0.5, height / 2 + 0.06, 0);
book.rotation.z = i === 6 ? 0.18 : 0; // one book leaning
shelf.add(book);
});
scene.add(shelf);
renderer.setAnimationLoop((time) => {
shelf.rotation.y = Math.sin(time / 1800) * 0.5;
renderer.render(scene, camera);
});
</script>