Three.js Tutorial: Installation, Scene Setup, Model Loading, Interaction, and Optimization
This article provides a step‑by‑step guide on using three.js—including installing the library, creating a scene with an orthographic camera, loading GLTF models, implementing touch‑based rotation, zoom, and pan interactions, adding lighting and rendering optimizations, and handling iOS Safari scaling issues.
The author needed three.js for a recent project and created this self‑study tutorial covering model preview, environment mapping, scaling, translation, lighting, and visual clarity adjustments.
1. Installation Install three.js via npm install three or yarn add three and import the core library with import * as THREE from 'three'; .
2. Scene Creation The HTML template contains a <div ref="container"></div> placeholder. In the script a scene, an orthographic camera, and a WebGL renderer with transparent background are created. Optional HDR environment mapping can be added using RGBELoader . An animation loop renders the scene, and a resize listener updates the renderer and camera.
<template>
<div ref="container"></div>
</template> const container = ref(null)
// Create scene
const scene = new THREE.Scene();
// Create orthographic camera to avoid perspective distortion
camera = new THREE.OrthographicCamera(
window.innerWidth / -10.5,
window.innerWidth / 10.5,
window.innerHeight / 10.5,
window.innerHeight / -10.5,
0.1,
1000
);
camera.position.z = 200;
renderer = new THREE.WebGLRenderer({ alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x000000, 0);
container.value.appendChild(renderer.domElement);
// Animation loop
const animate = () => {
requestAnimationFrame(animate);
renderer.render(scene, camera);
};
animate();
// Resize handling
window.addEventListener("resize", () => {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
});3. Model Import Models can be downloaded from sites like sketchfab.com. The GLTF/DRACO loader code imports the model, centers it, and adds it to the scene.
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
const modelContainer = new THREE.Object3D();
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('./draco/');
loader.setDRACOLoader(dracoLoader);
const modelPath = url; // URL of the GLB model
loader.load(
modelPath,
(gltf) => {
model = gltf.scene;
const box = new THREE.Box3().setFromObject(model);
const center = box.getCenter(new THREE.Vector3());
model.position.sub(center);
modelContainer.add(model);
scene.add(modelContainer);
},
(xhr) => {},
(error) => { console.error("Failed to load model", error); }
);4. Interaction Events Touch handling is implemented with a state machine (NONE, MOVE, ZOOM_OR_PAN). Variables store start positions, pointer distance, and rotation speed. Handlers manage touch start, move, and end to rotate, zoom, pan, and reset the model. An auto‑rotate timer and a reset function are also provided.
// State definitions
const STATE = { NONE: -1, MOVE: 0, ZOOM_OR_PAN: 1, POSITION_MOVE: 1 };
const mouseInfo = ref({ startX: 0, startY: 0, isDown: false, startPointerDistance: 0, state: STATE.NONE });
const rotateSpeed = 0.010;
// Touch start
renderer.domElement.addEventListener('touchstart', (e) => { handleTouchStart(e); });
function handleTouchStart(event) { /* ... */ }
// Touch move
renderer.domElement.addEventListener('touchmove', (e) => { handleTouchMove(e); });
function handleTouchMove(event) { /* ... */ }
// Touch end
renderer.domElement.addEventListener('touchend', () => { handleTouchEnd(); });
function handleTouchEnd() { /* reset state */ }
// Auto‑rotate
const rotateTimer = ref(null);
function setTimer() { rotateTimer.value = setInterval(() => { modelContainer.rotation.y -= 0.1; }, 100); }
// Reset position
function ResetPosition() { /* reset position, scale, rotation */ }5. Optimization and Adjustments Lighting includes an ambient light, a directional light targeting the model, and an optional point light. Antialiasing and pixel‑ratio settings improve visual quality. A Safari‑specific script prevents double‑tap zoom and gesture conflicts on iOS.
// Lighting
const ambientLight = new THREE.AmbientLight(0xffffff, 2);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 2);
directionalLight.position.set(1,1,80);
directionalLight.target = modelContainer;
scene.add(directionalLight);
var pointLight = new THREE.PointLight(0xffffff, 500);
pointLight.position.set(0,0,100);
scene.add(pointLight);
// Antialiasing and resolution
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setPixelRatio(window.devicePixelRatio);
// iOS Safari scaling fix
window.onload = function() {
var lastTouchEnd = 0;
document.addEventListener('touchstart', function(event) {
if (event.touches.length > 1) event.preventDefault();
});
document.addEventListener('touchend', function(event) {
var now = Date.now();
if (now - lastTouchEnd <= 300) event.preventDefault();
lastTouchEnd = now;
}, false);
document.addEventListener('gesturestart', function(event) { event.preventDefault(); });
document.addEventListener('dblclick', function(event) { event.preventDefault(); });
};6. References Model sources: sketchfab.com or market.pmnd.rs . HDR environment maps: hdrmaps.com . three.js documentation: threejs docs .
7. Conclusion The author invites readers to comment with questions and asks for a like to support future articles.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.