/threejs-interaction
Three.js interaction - raycasting, controls, mouse/touch input, object selection. Use when handling user input, implementing click detection, adding camera controls, or creating interactive 3D experiences.
$ npx -y skills add cloudai-x/threejs-skills --skill threejs-interaction --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/threejs-interaction
Context preview
The summary Claude sees to decide when to auto-load this skill.
Three.js interaction - raycasting, controls, mouse/touch input, object selection. Use when handling user input, implementing click detection, adding camera controls, or creating interactive 3D experiences.
SKILL.md
threejs-interaction.SKILL.mdname: threejs-interaction
description: Three.js interaction - raycasting, controls, mouse/touch input, object selection. Use when handling user input, implementing click detection, adding camera controls, or creating interactive 3D experiences.
Three.js Interaction
Quick Start
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
// Camera controls
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
// Raycasting for click detection
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
function onClick(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0) {
console.log("Clicked:", intersects[0].object);
}
}
window.addEventListener("click", onClick);Raycaster
Basic Raycasting
const raycaster = new THREE.Raycaster();
// From camera (mouse picking)
raycaster.setFromCamera(mousePosition, camera);
// From any origin and direction
raycaster.set(origin, direction); // origin: Vector3, direction: normalized Vector3
// Get intersections
const intersects = raycaster.intersectObjects(objects, recursive);
// intersects array contains:
// {
// distance: number, // Distance from ray origin
// point: Vector3, // Intersection point in world coords
// face: Face3, // Intersected face
// faceIndex: number, // Face index
// object: Object3D, // Intersected object
// uv: Vector2, // UV coordinates at intersection
// uv1: Vector2, // Second UV channel
// normal: Vector3, // Interpolated face normal
// instanceId: number // For InstancedMesh
// }Mouse Position Conversion
const mouse = new THREE.Vector2();
function updateMouse(event) {
// For full window
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
// For specific canvas element
function updateMouseCanvas(event, canvas) {
const rect = canvas.getBoundingClientRect();
mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
}Touch Support
function onTouchStart(event) {
event.preventDefault();
if (event.touches.length === 1) {
const touch = event.touches[0];
mouse.x = (touch.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(touch.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(clickableObjects);
if (intersects.length > 0) {
handleSelection(intersects[0]);
}
}
}
renderer.domElement.addEventListener("touchstart", onTouchStart);Raycaster Options
const raycaster = new THREE.Raycaster();
// Near/far clipping (default: 0, Infinity)
raycaster.near = 0;
raycaster.far = 100;
// Line/Points precision
raycaster.params.Line.threshold = 0.1;
raycaster.params.Points.threshold = 0.1;
// Layers (only intersect objects on specific layers)
raycaster.layers.set(1);
Efficient Raycasting
// Only check specific objects
const clickables = [mesh1, mesh2, mesh3];
const intersects = raycaster.intersectObjects(clickables, false);
// Use layers for filtering
mesh1.layers.set(1); // Clickable layer
raycaster.layers.set(1);
// Throttle raycast for hover effects
let lastRaycast = 0;
function onMouseMove(event) {
const now = Date.now();
if (now - lastRaycast < 50) return; // 20fps max
lastRaycast = now;
// Raycast here
}Camera Controls
OrbitControls
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
const controls = new OrbitControls(camera, renderer.domElement);
// Damping (smooth movement)
controls.enableDamping = true;
controls.dampingFactor = 0.05;
// Rotation limits
controls.minPolarAngle = 0; // Top
controls.maxPolarAngle = Math.PI / 2; // Horizon
controls.minAzimuthAngle = -Math.PI / 4; // Left
controls.maxAzimuthAngle = Math.PI / 4; // Right
// Zoom limits
controls.minDistance = 2;
controls.maxDistance = 50;
// Enable/disable features
controls.enableRotate = true;
controls.enableZoom = true;
controls.enablePan = true;
// Auto-rotate
controls.autoRotate = true;
controls.autoRotateSpeed = 2.0;
// Target (orbit point)
controls.target.set(0, 1, 0);
// Update in animation loop
function animate() {
controls.update(); // Required for damping and auto-rotate
renderer.render(scene, camera);
}FlyControls
import { FlyControls } from "three/addons/controls/FlyControls.js";
const controls = new FlyControls(camera, renderer.domElement);
controls.movementSpeed = 10;
controls.rollSpeed = Math.PI / 24;
controls.dragToLook = true;
// Update with delta
function animate() {
controls.update(clock.getDelta());
renderer.render(scene, camera);
}FirstPersonControls
import { FirstPersonControls } from "three/addons/controls/FirstPersonControls.js";
const controls = new FirstPersonControls(camera, renderer.domElement);
controls.movementSpeed = 10;
controls.lookSpeed = 0.1;
controls.lookVertical = true;
controls.constrainVertical = true;
controls.verticalMin = Math.PI / 4;
controls.verticalMax = (Math.PI * 3) / 4;
function animate() {
controls.update(clock.getDelta());
}PointerLockControls
import { PointerLockControls } from "three/addons/controls/PointerLockControls.js";
const controls = new PointerLockControls(camera, document.body);
// Lock pointer on click
document.addEventListener("click", () => {
controls.lock();
});
controls.addEventListener("lock", () => {
console.log("PointeRead more
name: threejs-interaction description: Three.js interaction - raycasting, controls, mouse/touch input, object selection. Use when handling user input, implementing click detection, adding camera controls, or creating interactive 3D experiences.
Three.js Interaction
Quick Start
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
// Camera controls
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
// Raycasting for click detection
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
function onClick(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0) {
console.log("Clicked:", intersects[0].object);
}
}
window.addEventListener("click", onClick);Raycaster
Basic Raycasting
const raycaster = new THREE.Raycaster();
// From camera (mouse picking)
raycaster.setFromCamera(mousePosition, camera);
// From any origin and direction
raycaster.set(origin, direction); // origin: Vector3, direction: normalized Vector3
// Get intersections
const intersects = raycaster.intersectObjects(objects, recursive);
// intersects array contains:
// {
// distance: number, // Distance from ray origin
// point: Vector3, // Intersection point in world coords
// face: Face3, // Intersected face
// faceIndex: number, // Face index
// object: Object3D, // Intersected object
// uv: Vector2, // UV coordinates at intersection
// uv1: Vector2, // Second UV channel
// normal: Vector3, // Interpolated face normal
// instanceId: number // For InstancedMesh
// }Mouse Position Conversion
const mouse = new THREE.Vector2();
function updateMouse(event) {
// For full window
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
// For specific canvas element
function updateMouseCanvas(event, canvas) {
const rect = canvas.getBoundingClientRect();
mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
}Touch Support
function onTouchStart(event) {
event.preventDefault();
if (event.touches.length === 1) {
const touch = event.touches[0];
mouse.x = (touch.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(touch.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(clickableObjects);
if (intersects.length > 0) {
handleSelection(intersects[0]);
}
}
}
renderer.domElement.addEventListener("touchstart", onTouchStart);Raycaster Options
const raycaster = new THREE.Raycaster(); // Near/far clipping (default: 0, Infinity) raycaster.near = 0; raycaster.far = 100; // Line/Points precision raycaster.params.Line.threshold = 0.1; raycaster.params.Points.threshold = 0.1; // Layers (only intersect objects on specific layers) raycaster.layers.set(1);
Efficient Raycasting
// Only check specific objects
const clickables = [mesh1, mesh2, mesh3];
const intersects = raycaster.intersectObjects(clickables, false);
// Use layers for filtering
mesh1.layers.set(1); // Clickable layer
raycaster.layers.set(1);
// Throttle raycast for hover effects
let lastRaycast = 0;
function onMouseMove(event) {
const now = Date.now();
if (now - lastRaycast < 50) return; // 20fps max
lastRaycast = now;
// Raycast here
}Camera Controls
OrbitControls
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
const controls = new OrbitControls(camera, renderer.domElement);
// Damping (smooth movement)
controls.enableDamping = true;
controls.dampingFactor = 0.05;
// Rotation limits
controls.minPolarAngle = 0; // Top
controls.maxPolarAngle = Math.PI / 2; // Horizon
controls.minAzimuthAngle = -Math.PI / 4; // Left
controls.maxAzimuthAngle = Math.PI / 4; // Right
// Zoom limits
controls.minDistance = 2;
controls.maxDistance = 50;
// Enable/disable features
controls.enableRotate = true;
controls.enableZoom = true;
controls.enablePan = true;
// Auto-rotate
controls.autoRotate = true;
controls.autoRotateSpeed = 2.0;
// Target (orbit point)
controls.target.set(0, 1, 0);
// Update in animation loop
function animate() {
controls.update(); // Required for damping and auto-rotate
renderer.render(scene, camera);
}FlyControls
import { FlyControls } from "three/addons/controls/FlyControls.js";
const controls = new FlyControls(camera, renderer.domElement);
controls.movementSpeed = 10;
controls.rollSpeed = Math.PI / 24;
controls.dragToLook = true;
// Update with delta
function animate() {
controls.update(clock.getDelta());
renderer.render(scene, camera);
}FirstPersonControls
import { FirstPersonControls } from "three/addons/controls/FirstPersonControls.js";
const controls = new FirstPersonControls(camera, renderer.domElement);
controls.movementSpeed = 10;
controls.lookSpeed = 0.1;
controls.lookVertical = true;
controls.constrainVertical = true;
controls.verticalMin = Math.PI / 4;
controls.verticalMax = (Math.PI * 3) / 4;
function animate() {
controls.update(clock.getDelta());
}PointerLockControls
import { PointerLockControls } from "three/addons/controls/PointerLockControls.js";
const controls = new PointerLockControls(camera, document.body);
// Lock pointer on click
document.addEventListener("click", () => {
controls.lock();
});
controls.addEventListener("lock", () => {
console.log("PointeA curated collection of Three.js skill files that provide Claude Code with foundational knowledge for creating 3D elements and interactive experiences.
Repo: cloudai-x/threejs-skills
Other skills on threejs-skills.
- /threejs-animation
Three.js animation - keyframe animation, skeletal animation, morph targets, animation mixing. Use when animating objects, playing GLTF animations, creating procedural motion, or blending animations.
Open skill - /threejs-fundamentals
Three.js scene setup, cameras, renderer, Object3D hierarchy, coordinate systems. Use when setting up 3D scenes, creating cameras, configuring renderers, managing object hierarchies, or working with transforms.
Open skill - /threejs-geometry
Three.js geometry creation - built-in shapes, BufferGeometry, custom geometry, instancing. Use when creating 3D shapes, working with vertices, building custom meshes, or optimizing with instanced rendering.
Open skill - /threejs-lighting
Three.js lighting - light types, shadows, environment lighting. Use when adding lights, configuring shadows, setting up IBL, or optimizing lighting performance.
Open skill - /threejs-loaders
Three.js asset loading - GLTF, textures, images, models, async patterns. Use when loading 3D models, textures, HDR environments, or managing loading progress.
Open skill - /threejs-materials
Three.js materials - PBR, basic, phong, shader materials, material properties. Use when styling meshes, working with textures, creating custom shaders, or optimizing material performance.
Open skill

