The core generates 21 shapes, from BoxGeometry and SphereGeometry to path-based LatheGeometry, ExtrudeGeometry and TubeGeometry. Segment arguments trade smoothness for triangles: SphereGeometry(r, 32, 16) has 960, (r, 64, 32) about four times as many. renderFrames(), added to bookshelf.js, runs setAnimationLoop's loop for three frames and then sets window.__done:
<script src="three-map.js"></script>
<script type="module">
import * as THREE from 'three';
import { BOOKS, createStage, renderFrames } from './bookshelf.js';
const { renderer, scene, camera } = createStage({ width: 640, height: 110 });
camera.fov = 12; // a long lens flattens the row
camera.updateProjectionMatrix();
camera.position.set(0, 1.2, 6);
camera.lookAt(0, 0, 0);
[
new THREE.BoxGeometry(0.55, 0.55, 0.55), new THREE.SphereGeometry(0.34, 32, 16),
new THREE.CylinderGeometry(0.28, 0.28, 0.7, 32), new THREE.ConeGeometry(0.34, 0.7, 32),
new THREE.CapsuleGeometry(0.2, 0.3, 8, 16), new THREE.TorusGeometry(0.27, 0.1, 16, 48),
new THREE.IcosahedronGeometry(0.38), new THREE.TorusKnotGeometry(0.22, 0.07, 96, 12),
].forEach((geometry, i) => {
const color = BOOKS[i % 6].color;
const mesh = new THREE.Mesh(geometry, new THREE.MeshStandardMaterial({ color }));
mesh.position.x = (i - 3.5) * 0.85;
scene.add(mesh);
});
renderFrames(renderer, scene, camera);
</script>
Add-ons supply RoundedBoxGeometry, TextGeometry, ConvexGeometry, DecalGeometry and more. Share one geometry among identical meshes: each new allocates and uploads its own buffers.
<!doctype html>
<style>
body { margin: 0; background: #f5f1e8; font: 13px system-ui, sans-serif; color: #263238; }
canvas { display: block; max-width: 100%; }
.ui { padding: 6px 8px; }
</style>
<script type="importmap">
{ "imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.186.1/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.186.1/examples/jsm/" } }
</script>
<div class="ui"><label><input type="checkbox" id="wire"> wireframe</label> <span id="out"></span></div>
<script type="module">
import * as THREE from 'three';
const colors = ['#1f5f8b', '#5b3f99', '#e09a10', '#3f7d3a', '#b5452f', '#2a9d8f'];
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
renderer.setSize(600, 200);
document.body.prepend(renderer.domElement);
const scene = new THREE.Scene();
scene.background = new THREE.Color('#fbf8f2');
const camera = new THREE.PerspectiveCamera(14, 3, 0.1, 50); // a long lens flattens the row
camera.position.set(0, 1.4, 12);
camera.lookAt(0, 0, 0);
scene.add(new THREE.HemisphereLight('#ffffff', '#8d6e63', 1.2));
const sun = new THREE.DirectionalLight('#ffffff', 2.5);
sun.position.set(2, 3, 4);
scene.add(sun);
const shapes = [
['Box', new THREE.BoxGeometry(0.55, 0.55, 0.55)],
['Sphere', new THREE.SphereGeometry(0.34, 32, 16)], // width and height segments
['Cylinder', new THREE.CylinderGeometry(0.28, 0.28, 0.7, 32)],
['Cone', new THREE.ConeGeometry(0.34, 0.7, 32)],
['Capsule', new THREE.CapsuleGeometry(0.2, 0.3, 8, 16)],
['Torus', new THREE.TorusGeometry(0.27, 0.1, 16, 48)],
['Icosahedron', new THREE.IcosahedronGeometry(0.38)],
['TorusKnot', new THREE.TorusKnotGeometry(0.22, 0.07, 96, 12)],
];
const meshes = shapes.map(([name, geometry], i) => {
const mesh = new THREE.Mesh(geometry, new THREE.MeshStandardMaterial({ color: colors[i % 6] }));
mesh.position.x = (i - 3.5) * 0.85;
scene.add(mesh);
return mesh;
});
// Triangles: index count / 3 for indexed geometry, else position count / 3
const triangles = (g) => (g.index ? g.index.count : g.attributes.position.count) / 3;
document.getElementById('out').textContent =
shapes.map(([name, g]) => `${name} ${triangles(g)}`).join(' · ');
document.getElementById('wire').addEventListener('change', (e) =>
meshes.forEach((m) => { m.material.wireframe = e.target.checked; }));
renderer.setAnimationLoop((time) => {
meshes.forEach((m, i) => { m.rotation.set(time / 2000 + i, time / 1500, 0); });
renderer.render(scene, camera);
});
</script>