Three.js 3D con Claude Code: guía práctica para Web UI
Crea un visor 3D con Claude Code y Three.js, con resize, cleanup, revisión y casos de uso reales.
Three.js facilita trabajar con WebGL, pero una interfaz 3D lista para producción no es solo un objeto que gira. También necesitas una cámara bien colocada, luces que expliquen la forma, un canvas que responda al tamaño del contenedor, límites de carga para móviles y una limpieza correcta de geometrías, materiales, controles y renderer cuando el componente se desmonta.
Claude Code ayuda mucho porque puede generar la base repetitiva de Three.js en minutos. El problema aparece cuando la petición es vaga: “hazlo en 3D” puede terminar en una demo bonita, pero con pantalla en blanco al cambiar el layout, fugas de memoria al navegar o un rendimiento pobre en teléfonos.
Esta guía muestra una base Vite + React + Three.js que se puede copiar y ejecutar, junto con prompts de revisión para que Claude Code actúe como segundo revisor antes de publicar.
Define primero el objetivo del 3D
Antes de escribir código, aclara qué necesita entender el usuario gracias al 3D. En un visor de producto, puede ser color, textura, tamaño y parte trasera. En visualización de datos, puede ser distribución, grupos o cambios en el tiempo. En una escena educativa o de portafolio, puede ser guiar la mirada por partes importantes.
Un buen prompt para Claude Code incluye restricciones concretas:
Crea un visor de producto 3D con Vite + React + TypeScript + three.
Requisitos:
- renderizar el canvas dentro de un elemento padre
- adaptarse al tamaño del contenedor
- permitir rotación y zoom con OrbitControls
- hacer dispose de geometry, material, renderer y controls al desmontar
- limitar devicePixelRatio a 2 en móviles
- entregar código que pueda copiarse en src/App.tsx
Estas restricciones forman el harness, es decir, el andamiaje que permite al agente trabajar sin romper la página. Con ese andamiaje se evitan fallos típicos: canvas en blanco, escena deformada, renderer vivo después de navegar y GPU móvil saturada. Para detalles de API, consulta la documentación oficial de Three.js y, en especial, WebGLRenderer.
Configuración mínima con Vite y React
Empieza con Three.js directo dentro de React. React Three Fiber es útil, pero para aprender y depurar conviene ver el ciclo de vida real: crear renderer, añadirlo al DOM, escuchar resize, animar y liberar recursos.
npm create vite@latest three-claude-demo -- --template react-ts
cd three-claude-demo
npm i three
npm run dev
flowchart LR
A["React component"] --> B["mount div"]
B --> C["WebGLRenderer canvas"]
C --> D["Scene"]
D --> E["Camera and lights"]
D --> F["Mesh and material"]
C --> G["OrbitControls"]
G --> H["resize and dispose"]
Visor 3D listo para copiar
Copia este código en src/App.tsx. Crea un objeto simple tipo producto, lo ilumina, permite rotación y zoom, se adapta al contenedor y libera recursos al desmontar.
import { useEffect, useRef } from "react";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import "./App.css";
export default function App() {
const mountRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {
const mount = mountRef.current;
if (!mount) return;
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xf6f7fb);
const camera = new THREE.PerspectiveCamera(45, 1, 0.1, 100);
camera.position.set(3.5, 2.2, 4.5);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.outputColorSpace = THREE.SRGBColorSpace;
renderer.shadowMap.enabled = true;
mount.appendChild(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.minDistance = 2.5;
controls.maxDistance = 8;
controls.target.set(0, 0.4, 0);
scene.add(new THREE.HemisphereLight(0xffffff, 0x7c8594, 1.6));
const keyLight = new THREE.DirectionalLight(0xffffff, 2.4);
keyLight.position.set(3, 5, 4);
keyLight.castShadow = true;
scene.add(keyLight);
const productGeometry = new THREE.BoxGeometry(1.8, 1.2, 1.1, 4, 4, 4);
const productMaterial = new THREE.MeshStandardMaterial({
color: 0x2f6f73,
roughness: 0.42,
metalness: 0.08,
});
const product = new THREE.Mesh(productGeometry, productMaterial);
product.castShadow = true;
product.position.y = 0.75;
scene.add(product);
const floorGeometry = new THREE.CircleGeometry(2.2, 64);
const floorMaterial = new THREE.MeshStandardMaterial({
color: 0xd9dee8,
roughness: 0.7,
});
const floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.rotation.x = -Math.PI / 2;
floor.receiveShadow = true;
scene.add(floor);
const resize = () => {
const width = mount.clientWidth;
const height = mount.clientHeight;
if (width === 0 || height === 0) return;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height, false);
};
let frameId = 0;
const clock = new THREE.Clock();
const animate = () => {
const elapsed = clock.getElapsedTime();
product.rotation.y = elapsed * 0.45;
product.rotation.x = Math.sin(elapsed * 0.8) * 0.08;
controls.update();
renderer.render(scene, camera);
frameId = window.requestAnimationFrame(animate);
};
resize();
animate();
window.addEventListener("resize", resize);
return () => {
window.removeEventListener("resize", resize);
window.cancelAnimationFrame(frameId);
controls.dispose();
scene.traverse((object) => {
if (object instanceof THREE.Mesh) {
object.geometry.dispose();
const materials = Array.isArray(object.material)
? object.material
: [object.material];
materials.forEach((material) => material.dispose());
}
});
renderer.dispose();
renderer.domElement.remove();
};
}, []);
return (
<main className="viewerShell">
<div className="copy">
<p className="eyebrow">Three.js + Claude Code</p>
<h1>3D product viewer</h1>
<p>
Drag to rotate, scroll to zoom, and resize the window to verify that
the canvas follows its container.
</p>
</div>
<div ref={mountRef} className="viewerStage" />
</main>
);
}
Añade este CSS en src/App.css. La altura del contenedor es obligatoria: si el padre mide cero, el canvas existe pero parece una pantalla en blanco.
body {
margin: 0;
font-family: Inter, system-ui, sans-serif;
background: #eef2f7;
color: #17202a;
}
.viewerShell {
min-height: 100vh;
display: grid;
grid-template-columns: minmax(260px, 0.8fr) minmax(320px, 1.2fr);
gap: 32px;
align-items: center;
padding: 40px;
box-sizing: border-box;
}
.copy {
max-width: 520px;
}
.viewerStage {
height: min(62vh, 560px);
min-height: 360px;
border: 1px solid #ccd5df;
background: #f6f7fb;
}
.viewerStage canvas {
display: block;
}
@media (max-width: 760px) {
.viewerShell {
grid-template-columns: 1fr;
padding: 24px;
}
.viewerStage {
min-height: 300px;
}
}
Pide a Claude Code una revisión técnica
Después de implementar, no pidas solo “mejorar el diseño”. Pide revisar riesgos concretos.
Revisa esta implementación React + Three.js.
Comprueba:
1. si el canvas sigue el tamaño del contenedor
2. si geometry, material, renderer y controls se liberan al desmontar
3. si requestAnimationFrame se detiene
4. si devicePixelRatio es seguro para pantallas móviles densas
5. si window/document romperían SSR en Next.js o Astro
6. si cámara, luces y controles sirven para inspeccionar un producto
Devuelve diffs concretos y una lista de pruebas manuales.
Este prompt convierte a Claude Code en revisor. Es importante porque una fuga de memoria no siempre se ve en una captura de pantalla. Navega varias veces fuera de la página y observa Memory y Performance en DevTools.
Tres casos de uso reales
| Caso | Valor del 3D | Trabajo para Claude Code |
|---|---|---|
| Visor de producto | El comprador revisa color, acabado, profundidad y parte trasera | OrbitControls, variantes de color, luces, pruebas móviles |
| Visualización de datos | Muestra grupos, valores atípicos o movimiento temporal | nubes de puntos, barras 3D, transición de cámara, leyendas |
| Portafolio o educación | Explica piezas y estructura con una escena manipulable | etiquetas, resaltado de partes, vistas guiadas |
En productos, el 3D debe reducir dudas de compra. En datos, no siempre conviene: si la perspectiva dificulta comparar valores, incluye una vista 2D. En educación, agrega secuencia: botones de cámara, etiquetas y pausa de rotación cuando se lee una explicación.
Fallos comunes y soluciones
| Fallo | Causa probable | Solución |
|---|---|---|
| Canvas en blanco | altura del padre en cero, cámara mal orientada o falta de luz | fija altura CSS, revisa camera position y controls target |
| Escena deformada tras resize | no se actualizan camera.aspect y renderer size juntos | usa camera.updateProjectionMatrix() y renderer.setSize() |
| La página se vuelve lenta | no se hizo dispose de recursos | recorre la scene al desmontar y libera geometry/material |
| El móvil se calienta | pixel ratio alto, sombras caras o demasiados polígonos | limita pixelRatio, reduce sombras y segmentos |
| Error en SSR | acceso a window/document durante render del servidor | inicializa Three.js dentro de useEffect o un componente client-only |
Para depurar una pantalla en blanco, elimina complejidad: fondo claro, cubo, una luz y una cámara. No investigues al mismo tiempo loader GLB, HDR, postprocesado y layout.
Checklist de publicación y CTA
Antes de publicar, prueba el dispositivo más débil que quieras soportar. Comprueba fps, temperatura, peso del modelo, fallback si WebGL falla y explicación alternativa para usuarios que no interactúan con el canvas. Para arquitectura de canvas, revisa la guía de Canvas con Claude Code. Para ajustar movimiento, consulta la guía de animación con Claude Code.
Claude Code Lab puede ayudarte a revisar visores 3D de producto, visualizaciones WebGL y escenas educativas, además de entrenar equipos en prompts de revisión. Lleva a la consulta los dispositivos objetivo, framework, formato de modelo, presupuesto de rendimiento y razón de negocio para usar 3D.
Resultado probado
Probé el código pegándolo en un proyecto Vite React TypeScript y cambiando entre ancho de escritorio y móvil. La altura explícita de .viewerStage evitó el fallo típico de canvas en blanco. El límite de devicePixelRatio a 2 redujo trabajo innecesario de GPU en pantallas densas, y la revisión de Claude Code sobre dispose ayudó a detectar riesgos antes de considerar la demo lista para producción.
PDF gratis: cheatsheet de Claude Code
Introduce tu email y descarga una hoja con comandos, hábitos de revisión y flujos seguros.
Cuidamos tus datos y no enviamos spam.
Sobre el autor
Masa
Ingeniero enfocado en workflows prácticos con Claude Code.
Artículos relacionados
Workflow de Obsidian a CLAUDE.md con Claude Code
Convierte notas de trabajo de Obsidian en notas operativas de CLAUDE.md para no repetir contexto.
Claude Code Revenue CTA Routing: de artículos a PDF, Gumroad y consulta
Un flujo con Claude Code para dirigir lectores a PDF gratis, Gumroad o consulta según intención.
Reglas de handoff para equipos con Claude Code: evidencia, permisos, rollback e ingresos
Formato práctico para entregar trabajo de Claude Code con pruebas, permisos, rollback, PDF gratis, Gumroad y consulta.