本文へ移動
cccskills
無料GitHub で公開

threejs-skills

Create 3D scenes, interactive experiences, and visual effects using Three.js. Use when user requests 3D graphics, WebGL experiences, 3D visualizations, animations, or interactive 3D elements.

インストール方法を見る

含まれるファイル(1)

  • SKILL.md19.9 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Three.js Skills

Systematically create high-quality 3D scenes and interactive experiences using Three.js best practices.

When to Use

  • Requests 3D visualizations or graphics ("create a 3D model", "show in 3D")
  • Wants interactive 3D experiences ("rotating cube", "explorable scene")
  • Needs WebGL or canvas-based rendering
  • Asks for animations, particles, or visual effects
  • Mentions Three.js, WebGL, or 3D rendering
  • Wants to visualize data in 3D space

Core Setup Pattern

1. Essential Three.js Imports

Use ES module import maps for modern Three.js (r183+):

<script type="importmap">
{
  "imports": {
    "three": "https://cdn.jsdelivr.net/npm/three@0.183.0/build/three.module.js",
    "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.183.0/examples/jsm/"
  }
}
</script>
<script type="module">
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
</script>

For production with npm/vite/webpack:

import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";

2. Scene Initialization

Every Three.js artifact needs these core components:

// Scene - contains all 3D objects
const scene = new THREE.Scene();

// Camera - defines viewing perspective
const camera = new THREE.PerspectiveCamera(
  75, // Field of view
  window.innerWidth / window.innerHeight, // Aspect ratio
  0.1, // Near clipping plane
  1000, // Far clipping plane
);
camera.position.z = 5;

// Renderer - draws the scene
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

3. Animation Loop

Use renderer.setAnimationLoop() (preferred) or requestAnimationFrame:

// Preferred: setAnimationLoop (handles WebXR compatibility)
renderer.setAnimationLoop(() => {
  mesh.rotation.x += 0.01;
  mesh.rotation.y += 0.01;
  renderer.render(scene, camera);
});

// Alternative: manual requestAnimationFrame
function animate() {
  requestAnimationFrame(animate);
  mesh.rotation.x += 0.01;
  mesh.rotation.y += 0.01;
  renderer.render(scene, camera);
}
animate();

Systematic Development Process

1. Define the Scene

Start by identifying:

  • What objects need to be rendered
  • Camera position and field of view
  • Lighting setup required
  • Interaction model (static, rotating, user-controlled)

2. Build Geometry

Choose appropriate geometry types:

Basic Shapes:

  • BoxGeometry - cubes, rectangular prisms
  • SphereGeometry - spheres, planets
  • CylinderGeometry - cylinders, tubes
  • PlaneGeometry - flat surfaces, ground planes
  • TorusGeometry - donuts, rings

CapsuleGeometry is available (stable since r142):

new THREE.CapsuleGeometry(0.5, 1, 4, 8); // radius, length, capSegments, radialSegments

3. Apply Materials

Choose materials based on visual needs:

Common Materials:

  • MeshBasicMaterial - unlit, flat colors (no lighting needed)
  • MeshStandardMaterial - physically-based, realistic (needs lighting)
  • MeshPhongMaterial - shiny surfaces with specular highlights
  • MeshLambertMaterial - matte surfaces, diffuse reflection
const material = new THREE.MeshStandardMaterial({
  color: 0x00ff00,
  metalness: 0.5,
  roughness: 0.5,
});

4. Add Lighting

If using lit materials (Standard, Phong, Lambert), add lights:

// Ambient light - general illumination
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);

// Directional light - like sunlight
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(5, 5, 5);
scene.add(directionalLight);

Skip lighting if using MeshBasicMaterial - it's unlit by design.

5. Handle Responsiveness

Always add window resize handling:

window.addEventListener("resize", () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
});

Common Patterns

Rotating Object

function animate() {
  requestAnimationFrame(animate);
  mesh.rotation.x += 0.01;
  mesh.rotation.y += 0.01;
  renderer.render(scene, camera);
}

OrbitControls

With import maps or build tools, OrbitControls works directly:

import { OrbitControls } from "three/addons/controls/OrbitControls.js";

const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;

// Update in animation loop
renderer.setAnimationLoop(() => {
  controls.update();
  renderer.render(scene, camera);
});

Custom Camera Controls (Alternative)

For lightweight custom controls without importing OrbitControls:

let isDragging = false;
let previousMousePosition = { x: 0, y: 0 };

renderer.domElement.addEventListener("mousedown", () => {
  isDragging = true;
});

renderer.domElement.addEventListener("mouseup", () => {
  isDragging = false;
});

renderer.domElement.addEventListener("mousemove", (event) => {
  if (isDragging) {
    const deltaX = event.clientX - previousMousePosition.x;
    const deltaY = event.clientY - previousMousePosition.y;

    // Rotate camera around scene
    const rotationSpeed = 0.005;
    camera.position.x += deltaX * rotationSpeed;
    camera.position.y -= deltaY * rotationSpeed;
    camera.lookAt(scene.position);
  }

  previousMousePosition = { x: event.clientX, y: event.clientY };
});

// Zoom with mouse wheel
renderer.domElement.addEventListener("wheel", (event) => {
  event.preventDefault();
  camera.position.z += event.deltaY * 0.01;
  camera.position.z = Math.max(2, Math.min(20, camera.position.z)); // Clamp
});

Raycasting for Object Selection

Detect mouse clicks and hovers on 3D objects:

const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
const clickableObjects = []; // Array of meshes that can be clicked

// Update mouse position
window.addEventListener("mousemove", (event) => {
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
});

// Detect clicks
window.addEventListener("click", () => {
  raycaster.setFromCamera(mouse, camera);
  const intersects = raycaster.intersectObjects(clickableObjects);

  if (intersects.length > 0) {
    const clickedObject = intersects[0].object;
    // Handle click - change color, scale, etc.
    clickedObject.material.color.set(0xff0000);
  }
});

// Hover effect in animation loop
function animate() {
  requestAnimationFrame(animate);

  raycaster.setFromCamera(mouse, camera);
  const intersects = raycaster.intersectObjects(clickableObjects);

  // Reset all objects
  clickableObjects.forEach((obj) => {
    obj.scale.set(1, 1, 1);
  });

  // Highlight hovered object
  if (intersects.length > 0) {
    intersects[0].object.scale.set(1.2, 1.2, 1.2);
    document.body.style.cursor = "pointer";
  } else {
    document.body.style.cursor = "default";
  }

  renderer.render(scene, camera);
}

Particle System

const particlesGeometry = new THREE.BufferGeometry();
const particlesCount = 1000;
const posArray = new Float32Array(particlesCount * 3);

for (let i = 0; i < particlesCount * 3; i++) {
  posArray[i] = (Math.random() - 0.5) * 10;
}

particlesGeometry.setAttribute(
  "position",
  new THREE.BufferAttribute(posArray, 3),
);

const particlesMaterial = new THREE.PointsMaterial({
  size: 0.02,
  color: 0xffffff,
});

const particlesMesh = new THREE.Points(particlesGeometry, particlesMaterial);
scene.add(particlesMesh);

User Interaction (Mouse Movement)

let mouseX = 0;
let mouseY = 0;

document.addEventListener("mousemove", (event) => {
  mouseX = (event.clientX / window.innerWidth) * 2 - 1;
  mouseY = -(event.clientY / window.innerHeight) * 2 + 1;
});

function animate() {
  requestAnimationFrame(animate);
  camera.position.x = mouseX * 2;
  camera.position.y = mouseY * 2;
  camera.lookAt(scene.position);
  renderer.render(scene, camera);
}

Loading Textures

const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load("texture-url.jpg");

const material = new THREE.MeshStandardMaterial({
  map: texture,
});

Best Practices

Performance

  • Reuse geometries and materials when creating multiple similar objects
  • Use BufferGeometry for custom shapes (more efficient)
  • Limit particle counts to maintain 60fps (start with 1000-5000)
  • Dispose of resources when removing objects:
    geometry.dispose();
    material.dispose();
    texture.dispose();
    

Visual Quality

  • Always set antialias: true on renderer for smooth edges
  • Use appropriate camera FOV (45-75 degrees typical)
  • Position lights thoughtfully - avoid overlapping multiple bright lights
  • Add ambient + directional lighting for realistic scenes

Code Organization

  • Initialize scene, camera, renderer at the top
  • Group related objects (e.g., all particles in one group)
  • Keep animation logic in the animate function
  • Separate object creation into functions for complex scenes

Common Pitfalls to Avoid

  • ❌ Using outputEncoding instead of outputColorSpace (renamed in r152)
  • ❌ Forgetting to add objects to scene with scene.add()
  • ❌ Using lit materials without adding lights
  • ❌ Not handling window resize
  • ❌ Forgetting to call renderer.render() in animation loop
  • ❌ Using THREE.Clock without considering THREE.Timer (recommended in r183)

Example Workflow

User: "Create an interactive 3D sphere that responds to mouse movement"

  1. Setup: Import Three.js, create scene/camera/renderer
  2. Geometry: Create SphereGeometry(1, 32, 32) for smooth sphere
  3. Material: Use MeshStandardMaterial for realistic look
  4. Lighting: Add ambient + directional lights
  5. Interaction: Track mouse position, update camera
  6. Animation: Rotate sphere, render continuously
  7. Responsive: Add window resize handler
  8. Result: Smooth, interactive 3D sphere ✓

Troubleshooting

Black screen / Nothing renders:

  • Check if objects added to scene
  • Verify camera position isn't inside objects
  • Ensure renderer.render() is called
  • Add lights if using lit materials

Poor performance:

  • Reduce particle count
  • Lower geometry detail (segments)
  • Reuse materials/geometries
  • Check browser console for errors

Objects not visible:

  • Check object position vs camera position
  • Verify material has visible color/properties
  • Ensure camera far plane includes objects
  • Add lighting if needed

Advanced Techniques

Visual Polish for Portfolio-Grade Rendering

Shadows:

// Enable shadows on renderer
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // Soft shadows

// Light that casts shadows
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 10, 5);
directionalLight.castShadow = true;

// Configure shadow quality
directionalLight.shadow.mapSize.width = 2048;
directionalLight.shadow.mapSize.height = 2048;
directionalLight.shadow.camera.near = 0.5;
directionalLight.shadow.camera.far = 50;

scene.add(directionalLight);

// Objects cast and receive shadows
mesh.castShadow = true;
mesh.receiveShadow = true;

// Ground plane receives shadows
const groundGeometry = new THREE.PlaneGeometry(20, 20);
const groundMaterial = new THREE.MeshStandardMaterial({ color: 0x808080 });
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.rotation.x = -Math.PI / 2;
ground.receiveShadow = true;
scene.add(ground);

Environment Maps & Reflections:

// Create environment map from cubemap
const loader = new THREE.CubeTextureLoader();
const envMap = loader.load([
  "px.jpg",
  "nx.jpg", // positive x, negative x
  "py.jpg",
  "ny.jpg", // positive y, negative y
  "pz.jpg",
  "nz.jpg", // positive z, negative z
]);

scene.environment = envMap; // Affects all PBR materials
scene.background = envMap; // Optional: use as skybox

// Or apply to specific materials
const material = new THREE.MeshStandardMaterial({
  metalness: 1.0,
  roughness: 0.1,
  envMap: envMap,
});

Tone Mapping & Output Encoding:

// Improve color accuracy and HDR rendering
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.0;
renderer.outputColorSpace = THREE.SRGBColorSpace; // Was outputEncoding in older versions

// Makes colors more vibrant and realistic

Fog for Depth:

// Linear fog
scene.fog = new THREE.Fog(0xcccccc, 10, 50); // color, near, far

// Or exponential fog (more realistic)
scene.fog = new THREE.FogExp2(0xcccccc, 0.02); // color, density

Custom Geometry from Vertices

const geometry = new THREE.BufferGeometry();
const vertices = new Float32Array([-1, -1, 0, 1, -1, 0, 1, 1, 0]);
geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));

Post-Processing Effects

Post-processing effects are available via import maps or build tools. See threejs-postprocessing skill for EffectComposer, bloom, DOF, and more.

Group Objects

const group = new THREE.Group();
group.add(mesh1);
group.add(mesh2);
group.rotation.y = Math.PI / 4;
scene.add(group);

Summary

Three.js artifacts require systematic setup:

  1. Import Three.js via import maps or build tools
  2. Initialize scene, camera, renderer
  3. Create geometry + material = mesh
  4. Add lighting if using lit materials
  5. Implement animation loop (prefer setAnimationLoop)
  6. Handle window resize
  7. Set renderer.outputColorSpace = THREE.SRGBColorSpace

Follow these patterns for reliable, performant 3D experiences.

Modern Three.js Practices (r183)

Modular Imports

// With npm/vite/webpack:
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";

WebGPU Renderer (Alternative)

Three.js r183 includes a WebGPU renderer as an alternative to WebGL:

import { WebGPURenderer } from "three/addons/renderers/webgpu/WebGPURenderer.js";

const renderer = new WebGPURenderer({ antialias: true });
await renderer.init();
renderer.setSize(window.innerWidth, window.innerHeight);

WebGPU uses TSL (Three.js Shading Language) instead of GLSL for custom shaders. See threejs-shaders for details.

Timer (r183 Recommended)

THREE.Timer is recommended over THREE.Clock as of r183:

const timer = new THREE.Timer();

renderer.setAnimationLoop(() => {
  timer.update();
  const delta = timer.getDelta();
  const elapsed = timer.getElapsed();

  mesh.rotation.y += delta;
  renderer.render(scene, camera);
});

Benefits over Clock:

  • Not affected by page visibility (pauses when tab is hidden)
  • Cleaner API design
  • Better integration with setAnimationLoop

Animation Libraries (GSAP Integration)

// Smooth timeline-based animations
import gsap from "gsap";

// Instead of manual animation loops:
gsap.to(mesh.position, {
  x: 5,
  duration: 2,
  ease: "power2.inOut",
});

// Complex sequences:
const timeline = gsap.timeline();
timeline
  .to(mesh.rotation, { y: Math.PI * 2, duration: 2 })
  .to(mesh.scale, { x: 2, y: 2, z: 2, duration: 1 }, "-=1");

Why GSAP:

  • Professional easing functions
  • Timeline control (pause, reverse, scrub)
  • Better than manual lerping for complex animations

Scroll-Based Interactions

// Sync 3D animations with page scroll
let scrollY = window.scrollY;

window.addEventListener("scroll", () => {
  scrollY = window.scrollY;
});

function animate() {
  requestAnimationFrame(animate);

  // Rotate based on scroll position
  mesh.rotation.y = scrollY * 0.001;

  // Move camera through scene
  camera.position.y = -(scrollY / window.innerHeight) * 10;

  renderer.render(scene, camera);
}

Advanced scroll libraries:

  • ScrollTrigger (GSAP plugin)
  • Locomotive Scroll
  • Lenis smooth scroll

Performance Optimization in Production

// Level of Detail (LOD)
const lod = new THREE.LOD();
lod.addLevel(highDetailMesh, 0); // Close up
lod.addLevel(mediumDetailMesh, 10); // Medium distance
lod.addLevel(lowDetailMesh, 50); // Far away
scene.add(lod);

// Instanced meshes for many identical objects
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshStandardMaterial();
const instancedMesh = new THREE.InstancedMesh(geometry, material, 1000);

// Set transforms for each instance
const matrix = new THREE.Matrix4();
for (let i = 0; i < 1000; i++) {
  matrix.setPosition(
    Math.random() * 100,
    Math.random() * 100,
    Math.random() * 100,
  );
  instancedMesh.setMatrixAt(i, matrix);
}

Modern Loading Patterns

// In production, load 3D models:
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";

const loader = new GLTFLoader();
loader.load("model.gltf", (gltf) => {
  scene.add(gltf.scene);

  // Traverse and setup materials
  gltf.scene.traverse((child) => {
    if (child.isMesh) {
      child.castShadow = true;
      child.receiveShadow = true;
    }
  });
});

When to Use What

Import Map Approach:

  • Quick prototypes and demos
  • Educational content
  • Artifacts and embedded experiences
  • No build step required

Production Build Approach:

  • Client projects and portfolios
  • Complex applications
  • Performance-critical applications
  • Team collaboration with version control

Recommended Production Stack

Three.js r183 + Vite
├── GSAP (animations)
├── React Three Fiber (optional - React integration)
├── Drei (helper components)
├── Leva (debug GUI)
└── Post-processing effects

Limitations

  • Use this skill only when the task clearly matches the scope described above.
  • Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
  • Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.

Overview

This skill dictates the systematic creation of high-quality 3D scenes, WebGL visualizations, and interactive particle systems using modern Three.js (r183+).

Core Process

  1. Initialize the Three.js scene, camera, and WebGL/WebGPU renderer with correct color space settings.
  2. Construct the required geometries, materials, and lighting arrays.
  3. Set up the animation loop using renderer.setAnimationLoop() and THREE.Timer.
  4. Implement responsive window resize handlers and explicit object disposal for memory management.

Common Rationalizations

RationalizationReality
"I'll use requestAnimationFrame, it's standard."renderer.setAnimationLoop() is required for WebXR compatibility and modern Three.js apps.
"I don't need to dispose of geometries, the garbage collector will get them."Three.js requires explicit .dispose() calls for geometries and materials to prevent WebGL memory leaks.
"I'll use THREE.Clock for the animation delta."THREE.Timer is recommended in r183+ as it correctly pauses when the tab is hidden.

Red Flags

  • Using outdated outputEncoding instead of outputColorSpace.
  • Forgetting to add window resize event listeners.
  • Creating lit materials (Standard/Phong) without adding ambient or directional lights to the scene.

Verification

  • The animation loop uses renderer.setAnimationLoop().
  • THREE.Timer is used instead of THREE.Clock for delta time.
  • The renderer has outputColorSpace = THREE.SRGBColorSpace configured.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Use when manage multiple local CLI agents via tmux sessions (start/stop/monitor/assign) with cron-friendly scheduling.

日本語の概要は準備中です。原文の説明を表示しています。

hybridlabor-api/aos62026年10月12日 更新

Use when a hybrid memory system that provides persistent, searchable knowledge management for AI agents (Architecture, Patterns, Decisions).

日本語の概要は準備中です。原文の説明を表示しています。

hybridlabor-api/aos62026年10月12日 更新

Reference for how BDB structures autonomous software engineering work — the seven-node dispatcher graph (Architect, TechLead, UI/UX, Engineering, Media/EventTech, Reviewer, Shipping) that /startcycle-graph actually runs. Use when you need the high-level lifecycle framing without inventing your own process.

日本語の概要は準備中です。原文の説明を表示しています。

hybridlabor-api/aos62026年10月12日 更新

Tools are how AI agents interact with the world. A well-designed tool is the difference between an agent that works and one that hallucinates, fails silently, or costs 10x more tokens than necessary. This skill covers tool design from schema to error handling.

日本語の概要は準備中です。原文の説明を表示しています。

hybridlabor-api/aos62026年10月12日 更新

Harness patterns for coding agents — memory, permissions, context engineering, delegation, skills, hooks, bootstrap.

日本語の概要は準備中です。原文の説明を表示しています。

hybridlabor-api/aos62026年10月12日 更新

agenttrail: live map of a multi-agent build in the browser: which plan component is being worked on, by which agent or harness, what is done and what is stuck. Use when a multi-agent pipeline starts (/startcycle, /startcycle-graph, /teamwork-preview) or after a plan-canvas approve, when the user asks to see what the agents are doing ("live map", "build board", "who is running now"), or by yourself whenever a multi-agent run is under way — no user prompt needed.

日本語の概要は準備中です。原文の説明を表示しています。

hybridlabor-api/aos62026年10月12日 更新

hybridlabor-api のスキルをすべて見る

このスキルの問題を報告する