webgl-3d-object — Claude Code skill: what it does, how to install
Guides coding agents to generate standalone Three.js 3D objects with physical materials, directional lighting, and proper memory cleanup.
npx skills add MengTo/Skills --skill webgl-3d-object -a claude-codeWhat webgl-3d-object does
This skill injects ready-to-use patterns for creating geometric WebGL objects in Three.js, such as faceted icosahedrons or custom meshes. The structure enforces perspective cameras, PBR materials (MeshStandardMaterial or MeshPhysicalMaterial), and dual-light setups using key and rim sources to highlight edges.
Motion handling respects user system settings via prefers-reduced-motion, stopping continuous floating or spinning when requested. The recipe explicitly demands teardown logic that calls dispose() on geometries, materials, and the renderer to stop memory leaks on component unmount.
Your project needs the three package installed for this code to run. The skill deliberately rules out fake CSS 3D transforms and warns against high shadow map resolutions that degrade mobile performance.
What it helps with
- render a real geometric 3D object inside a hero section
- configure Three.js PBR materials with key, rim, and ambient lighting
- handle smooth rotation while respecting prefers-reduced-motion
- clean up WebGL geometries, materials, and renderers on component unmount
Example prompts for the agent
- Build a faceted 3D hero object using Three.js with metallic material and rim lighting
- Create a floating 3D mesh in WebGL that properly cleans up resources on unmount
How to install webgl-3d-object
1Skills CLI (Vercel)
npx skills add MengTo/Skills --skill webgl-3d-object -a claude-code2Manually: put the files in place
~/.claude/skills/webgl-3d-object/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/webgl-3d-object/
1Skills CLI (Vercel)
npx skills add MengTo/Skills --skill webgl-3d-object -a codex2Manually: put the files in place
.agents/skills/webgl-3d-object/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add MengTo/Skills --skill webgl-3d-object -a cursor2Manually: put the files in place
.cursor/skills/webgl-3d-object/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install MengTo/Skills webgl-3d-object2Manually: put the files in place
.github/skills/webgl-3d-object/SKILL.md1Skills CLI (Vercel)
npx skills add MengTo/Skills --skill webgl-3d-object -a gemini-cli2Platform CLI
gemini skills install https://github.com/MengTo/Skills --path agent-skills/web-design/webgl-3d-objectManually: put the files in place
.devin/skills/webgl-3d-object/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add MengTo/Skills --skill webgl-3d-object -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about webgl-3d-object
How to install webgl-3d-object in Claude Code?
Run npx skills add MengTo/Skills --skill webgl-3d-object -a claude-code. Alternatively, place the skill file into ~/.claude/skills/webgl-3d-object/SKILL.md.
What does webgl-3d-object do?
It provides coding agents with conventions and recipes for building Three.js scenes with realistic depth, lighting, and shadow passes.
What dependencies does this skill require?
It requires the three library in your project, along with an HTML <canvas> element styled inside an aspect-ratio container.
Source text
SKILL.md · MIT
---
name: webgl-3d-object
description: Create a real 3D WebGL object with geometric mesh depth, physically based material, directional and ambient lighting, perspective camera, subtle rotation, and floating motion. Use when a page needs a faceted 3D hero object or product-like visual with real lighting instead of CSS transform tricks.
---
# WebGL 3D Object
## Use When
- A hero, feature block, or product moment needs one strong 3D object.
- The visual should show real geometry, lighting, highlights, and edges.
- A faceted mesh should float or rotate subtly inside a web layout.
- CSS transforms, SVG illusions, or flat gradients are not enough.
## Rules
1. Use real 3D geometry: `IcosahedronGeometry`, `DodecahedronGeometry`, `BoxGeometry`, custom `BufferGeometry`, or a glTF mesh.
2. Use a perspective camera so the object has depth and scale.
3. Use PBR material: `MeshStandardMaterial` or `MeshPhysicalMaterial`.
4. Tune `metalness`, `roughness`, and `emissive` to match the brand mood.
5. Light the object with at least one directional light plus ambient or hemisphere fill.
6. Animate transforms only: subtle rotation, bobbing, or parallax.
7. Handle resize and dispose geometry/material/renderer on teardown.
## HTML And CSS
```html
<div class="webgl-object-shell">
<canvas class="webgl-object-canvas" data-webgl-3d-object></canvas>
</div>
```
```css
.webgl-object-shell {
position: relative;
width: min(100%, 720px);
aspect-ratio: 1 / 1;
}
.webgl-object-canvas {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
display: block;
}
```
## Three.js Object Recipe
```js
import * as THREE from "three";
function initWebGL3DObject(canvas, options = {}) {
if (!canvas) return () => {};
const renderer = new THREE.WebGLRenderer({
canvas,
antialias: true,
alpha: true,
});
renderer.setClearColor(0x000000, 0);
renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, options.maxDpr || 1.75));
renderer.outputColorSpace = THREE.SRGBColorSpace;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = options.exposure || 1.05;
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(38, 1, 0.1, 100);
camera.position.set(0, 0.15, 5.2);
const geometry = new THREE.IcosahedronGeometry(options.radius || 1.35, options.detail || 1);
const material = new THREE.MeshStandardMaterial({
color: options.color || 0x8aa4ff,
metalness: options.metalness ?? 0.48,
roughness: options.roughness ?? 0.34,
emissive: options.emissive || 0x101833,
emissiveIntensity: options.emissiveIntensity ?? 0.22,
flatShading: true,
});
const object = new THREE.Mesh(geometry, material);
object.castShadow = true;
object.receiveShadow = true;
scene.add(object);
const ambient = new THREE.AmbientLight(0xffffff, 0.38);
scene.add(ambient);
const key = new THREE.DirectionalLight(0xffffff, 2.15);
key.position.set(3.4, 4.2, 4.8);
key.castShadow = true;
key.shadow.mapSize.set(1024, 1024);
scene.add(key);
const rim = new THREE.DirectionalLight(options.rimColor || 0x7dd3fc, 0.82);
rim.position.set(-4.2, 1.2, -2.8);
scene.add(rim);
const shadowPlane = new THREE.Mesh(
new THREE.PlaneGeometry(5.2, 5.2),
new THREE.ShadowMaterial({ opacity: 0.18 })
);
shadowPlane.position.set(0, -1.65, 0);
shadowPlane.rotation.x = -Math.PI / 2;
shadowPlane.receiveShadow = true;
scene.add(shadowPlane);
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
let rafId = 0;
function resize() {
const width = Math.max(1, canvas.clientWidth);
const height = Math.max(1, canvas.clientHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, options.maxDpr || 1.75));
renderer.setSize(width, height, false);
camera.aspect = width / height;
camera.updateProjectionMatrix();
}
function render(time = 0) {
const t = time * 0.001;
object.rotation.x = -0.16 + Math.sin(t * 0.45) * 0.06;
object.rotation.y = t * 0.28;
object.rotation.z = Math.sin(t * 0.32) * 0.08;
object.position.y = reduceMotion ? 0 : Math.sin(t * 0.8) * 0.08;
renderer.render(scene, camera);
if (!reduceMotion) rafId = requestAnimationFrame(render);
}
function handleResize() {
cancelAnimationFrame(rafId);
resize();
render();
}
resize();
render();
window.addEventListener("resize", handleResize);
return () => {
cancelAnimationFrame(rafId);
window.removeEventListener("resize", handleResize);
geometry.dispose();
material.dispose();
shadowPlane.geometry.dispose();
shadowPlane.material.dispose();
renderer.dispose();
};
}
const cleanupObject = initWebGL3DObject(
document.querySelector("[data-webgl-3d-object]"),
{
color: 0x8aa4ff,
rimColor: 0x7dd3fc,
metalness: 0.48,
roughness: 0.34,
emissive: 0x101833,
}
);
```
## Material Defaults
- Premium metal: `metalness: 0.45-0.7`, `roughness: 0.25-0.45`.
- Soft ceramic: `metalness: 0.0-0.15`, `roughness: 0.38-0.62`.
- Glow-tinted tech object: low `emissive` with `emissiveIntensity: 0.12-0.35`.
- Faceted object: set `flatShading: true`; smooth product object: set it to `false`.
## Lighting Defaults
- Key light: directional, high front-side angle, strongest source.
- Ambient fill: low intensity so shadows stay visible.
- Rim light: brand-tinted or cool light from behind to reveal edges.
- Shadows: enable only when the object needs grounded depth; keep map size moderate.
## Motion Defaults
- Rotation: slow, continuous, and secondary to the page content.
- Floating: `0.04` to `0.12` units on Y.
- Reduced motion: render a still frame or only allow direct interaction.
- Avoid camera movement unless the object is the main interaction.
## Avoid
- CSS 3D transforms pretending to be WebGL.
- Unlit materials when the ask is real lighting and depth.
- Flat planes with gradients instead of actual geometry.
- Strong bloom or particles that hide the form.
- High DPR, huge shadow maps, or too many lights on mobile.
- Letting the object compete with foreground copy or CTAs.
## Quick Checks
- The object has visible form, edges, highlights, and shadows.
- The material uses `metalness`, `roughness`, and optional `emissive`.
- Directional and ambient lights are both present.
- The camera is perspective, not orthographic by accident.
- Resize does not stretch the object.
- Geometry, material, event listeners, RAF, and renderer are cleaned up.
© MengTo · MIT · MengTo/Skills