Skip to main content 홈 크리에이터 beko2210 firstbrain threejs-interaction
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.
설치로 이동 Skills Marketplace 커뮤니티가 만든 AI 스킬을 발견하고 탐색하세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/BEKO2210/Firstbrain --skill threejs-interaction명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
Zip 다운로드 다운로드 중... 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. type skill created 2026-02-27T00:00:00.000Z domain design-creative category 3d-animation risk unknown source community tags ["skill","design-creative","3d-animation","threejs","interaction"]
Three.js Interaction
When to Use
You need user interaction inside a Three.js scene.
The task involves raycasting, object picking, pointer handling, touch input, or camera controls.
You are building an interactive 3D experience rather than a passive render.
Quick Start
import * as THREE from "three" ;
import { OrbitControls } from "three/addons/controls/OrbitControls.js" ;
const controls = new OrbitControls (camera, renderer.domElement );
controls.enableDamping = true ;
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. );
(intersects. > ) {
. ( , intersects[ ]. );
}
}
. ( , onClick);
children
if
length
0
console
log
"Clicked:"
0
object
window
addEventListener
"click"
Raycaster
Basic Raycasting const raycaster = new THREE .Raycaster ();
raycaster.setFromCamera (mousePosition, camera);
raycaster.set (origin, direction);
const intersects = raycaster.intersectObjects (objects, recursive);
Mouse Position Conversion const mouse = new THREE .Vector2 ();
function updateMouse (event ) {
mouse.x = (event.clientX / window .innerWidth ) * 2 - 1 ;
mouse.y = -(event.clientY / window .innerHeight ) * 2 + 1 ;
}
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 ();
raycaster.near = 0 ;
raycaster.far = 100 ;
raycaster.params .Line .threshold = 0.1 ;
raycaster.params .Points .threshold = 0.1 ;
raycaster.layers .set (1 );
Efficient Raycasting
const clickables = [mesh1, mesh2, mesh3];
const intersects = raycaster.intersectObjects (clickables, false );
mesh1.layers .set (1 );
raycaster.layers .set (1 );
let lastRaycast = 0 ;
function onMouseMove (event ) {
const now = Date .now ();
if (now - lastRaycast < 50 ) return ;
lastRaycast = now;
}
Camera Controls
OrbitControls import { OrbitControls } from "three/addons/controls/OrbitControls.js" ;
const controls = new OrbitControls (camera, renderer.domElement );
controls.enableDamping = true ;
controls.dampingFactor = 0.05 ;
controls.minPolarAngle = 0 ;
controls.maxPolarAngle = Math .PI / 2 ;
controls.minAzimuthAngle = -Math .PI / 4 ;
controls.maxAzimuthAngle = Math .PI / 4 ;
controls.minDistance = 2 ;
controls.maxDistance = 50 ;
controls.enableRotate = true ;
controls.enableZoom = true ;
controls.enablePan = true ;
controls.autoRotate = true ;
controls.autoRotateSpeed = 2.0 ;
controls.target .set (0 , 1 , 0 );
function animate ( ) {
controls.update ();
renderer.render (scene, camera);
}
OrbitControls Programmatic Methods (r183)
controls.dolly (1.5 );
controls.pan (deltaX, deltaY);
controls.rotate (deltaAzimuth, deltaPolar);
controls.cursorStyle = { orbit : "grab" , pan : "move" , dolly : "zoom-in" };
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 ;
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 );
document .addEventListener ("click" , () => {
controls.lock ();
});
controls.addEventListener ("lock" , () => {
console .log ("Pointer locked" );
});
controls.addEventListener ("unlock" , () => {
console .log ("Pointer unlocked" );
});
const velocity = new THREE .Vector3 ();
const direction = new THREE .Vector3 ();
const moveForward = false ;
const moveBackward = false ;
document .addEventListener ("keydown" , (event ) => {
switch (event.code ) {
case "KeyW" :
moveForward = true ;
break ;
case "KeyS" :
moveBackward = true ;
break ;
}
});
function animate ( ) {
if (controls.isLocked ) {
direction.z = Number (moveForward) - Number (moveBackward);
direction.normalize ();
velocity.z -= direction.z * 0.1 ;
velocity.z *= 0.9 ;
controls.moveForward (-velocity.z );
}
}
TrackballControls import { TrackballControls } from "three/addons/controls/TrackballControls.js" ;
const controls = new TrackballControls (camera, renderer.domElement );
controls.rotateSpeed = 2.0 ;
controls.zoomSpeed = 1.2 ;
controls.panSpeed = 0.8 ;
controls.staticMoving = true ;
function animate ( ) {
controls.update ();
}
MapControls import { MapControls } from "three/addons/controls/MapControls.js" ;
const controls = new MapControls (camera, renderer.domElement );
controls.enableDamping = true ;
controls.dampingFactor = 0.05 ;
controls.screenSpacePanning = false ;
controls.maxPolarAngle = Math .PI / 2 ;
TransformControls Gizmo for moving/rotating/scaling objects.
import { TransformControls } from "three/addons/controls/TransformControls.js" ;
const transformControls = new TransformControls (camera, renderer.domElement );
scene.add (transformControls);
transformControls.attach (selectedMesh);
transformControls.setMode ("translate" );
transformControls.setSpace ("local" );
transformControls.setSize (1 );
transformControls.addEventListener ("dragging-changed" , (event ) => {
orbitControls.enabled = !event.value ;
});
transformControls.addEventListener ("change" , () => {
renderer.render (scene, camera);
});
window .addEventListener ("keydown" , (event ) => {
switch (event.key ) {
case "g" :
transformControls.setMode ("translate" );
break ;
case "r" :
transformControls.setMode ("rotate" );
break ;
case "s" :
transformControls.setMode ("scale" );
break ;
case "Escape" :
transformControls.detach ();
break ;
}
});
DragControls import { DragControls } from "three/addons/controls/DragControls.js" ;
const draggableObjects = [mesh1, mesh2, mesh3];
const dragControls = new DragControls (
draggableObjects,
camera,
renderer.domElement ,
);
dragControls.addEventListener ("dragstart" , (event ) => {
orbitControls.enabled = false ;
event.object .material .emissive .set (0xaaaaaa );
});
dragControls.addEventListener ("drag" , (event ) => {
event.object .position .y = 0 ;
});
dragControls.addEventListener ("dragend" , (event ) => {
orbitControls.enabled = true ;
event.object .material .emissive .set (0x000000 );
});
Selection System
Click to Select const raycaster = new THREE .Raycaster ();
const mouse = new THREE .Vector2 ();
let selectedObject = null ;
function onMouseDown (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 (selectableObjects);
if (selectedObject) {
selectedObject.material .emissive .set (0x000000 );
}
if (intersects.length > 0 ) {
selectedObject = intersects[0 ].object ;
selectedObject.material .emissive .set (0x444444 );
} else {
selectedObject = null ;
}
}
Box Selection import { SelectionBox } from "three/addons/interactive/SelectionBox.js" ;
import { SelectionHelper } from "three/addons/interactive/SelectionHelper.js" ;
const selectionBox = new SelectionBox (camera, scene);
const selectionHelper = new SelectionHelper (renderer, "selectBox" );
document .addEventListener ("pointerdown" , (event ) => {
selectionBox.startPoint .set (
(event.clientX / window .innerWidth ) * 2 - 1 ,
-(event.clientY / window .innerHeight ) * 2 + 1 ,
0.5 ,
);
});
document .addEventListener ("pointermove" , (event ) => {
if (selectionHelper.isDown ) {
selectionBox.endPoint .set (
(event.clientX / window .innerWidth ) * 2 - 1 ,
-(event.clientY / window .innerHeight ) * 2 + 1 ,
0.5 ,
);
}
});
document .addEventListener ("pointerup" , (event ) => {
selectionBox.endPoint .set (
(event.clientX / window .innerWidth ) * 2 - 1 ,
-(event.clientY / window .innerHeight ) * 2 + 1 ,
0.5 ,
);
const selected = selectionBox.select ();
console .log ("Selected objects:" , selected);
});
Hover Effects const raycaster = new THREE .Raycaster ();
const mouse = new THREE .Vector2 ();
let hoveredObject = null ;
function onMouseMove (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 (hoverableObjects);
if (hoveredObject) {
hoveredObject.material .color .set (hoveredObject.userData .originalColor );
document .body .style .cursor = "default" ;
}
if (intersects.length > 0 ) {
hoveredObject = intersects[0 ].object ;
if (!hoveredObject.userData .originalColor ) {
hoveredObject.userData .originalColor =
hoveredObject.material .color .getHex ();
}
hoveredObject.material .color .set (0xff6600 );
document .body .style .cursor = "pointer" ;
} else {
hoveredObject = null ;
}
}
window .addEventListener ("mousemove" , onMouseMove);
Keyboard Input const keys = {};
document .addEventListener ("keydown" , (event ) => {
keys[event.code ] = true ;
});
document .addEventListener ("keyup" , (event ) => {
keys[event.code ] = false ;
});
function update ( ) {
const speed = 0.1 ;
if (keys["KeyW" ]) player.position .z -= speed;
if (keys["KeyS" ]) player.position .z += speed;
if (keys["KeyA" ]) player.position .x -= speed;
if (keys["KeyD" ]) player.position .x += speed;
if (keys["Space" ]) player.position .y += speed;
if (keys["ShiftLeft" ]) player.position .y -= speed;
}
World-Screen Coordinate Conversion
World to Screen function worldToScreen (position, camera ) {
const vector = position.clone ();
vector.project (camera);
return {
x : ((vector.x + 1 ) / 2 ) * window .innerWidth ,
y : (-(vector.y - 1 ) / 2 ) * window .innerHeight ,
};
}
const screenPos = worldToScreen (mesh.position , camera);
element.style .left = screenPos.x + "px" ;
element.style .top = screenPos.y + "px" ;
Screen to World function screenToWorld (screenX, screenY, camera, targetZ = 0 ) {
const vector = new THREE .Vector3 (
(screenX / window .innerWidth ) * 2 - 1 ,
-(screenY / window .innerHeight ) * 2 + 1 ,
0.5 ,
);
vector.unproject (camera);
const dir = vector.sub (camera.position ).normalize ();
const distance = (targetZ - camera.position .z ) / dir.z ;
return camera.position .clone ().add (dir.multiplyScalar (distance));
}
Ray-Plane Intersection function getRayPlaneIntersection (mouse, camera, plane ) {
const raycaster = new THREE .Raycaster ();
raycaster.setFromCamera (mouse, camera);
const intersection = new THREE .Vector3 ();
raycaster.ray .intersectPlane (plane, intersection);
return intersection;
}
const groundPlane = new THREE .Plane (new THREE .Vector3 (0 , 1 , 0 ), 0 );
const worldPos = getRayPlaneIntersection (mouse, camera, groundPlane);
Event Handling Best Practices class InteractionManager {
constructor (camera, renderer, scene ) {
this .camera = camera;
this .renderer = renderer;
this .scene = scene;
this .raycaster = new THREE .Raycaster ();
this .mouse = new THREE .Vector2 ();
this .clickables = [];
this .bindEvents ();
}
bindEvents ( ) {
const canvas = this .renderer .domElement ;
canvas.addEventListener ("click" , (e ) => this .onClick (e));
canvas.addEventListener ("mousemove" , (e ) => this .onMouseMove (e));
canvas.addEventListener ("touchstart" , (e ) => this .onTouchStart (e));
}
updateMouse (event ) {
const rect = this .renderer .domElement .getBoundingClientRect ();
this .mouse .x = ((event.clientX - rect.left ) / rect.width ) * 2 - 1 ;
this .mouse .y = -((event.clientY - rect.top ) / rect.height ) * 2 + 1 ;
}
getIntersects ( ) {
this .raycaster .setFromCamera (this .mouse , this .camera );
return this .raycaster .intersectObjects (this .clickables , true );
}
onClick (event ) {
this .updateMouse (event);
const intersects = this .getIntersects ();
if (intersects.length > 0 ) {
const object = intersects[0 ].object ;
if (object.userData .onClick ) {
object.userData .onClick (intersects[0 ]);
}
}
}
addClickable (object, callback ) {
this .clickables .push (object);
object.userData .onClick = callback;
}
dispose ( ) {
}
}
const interaction = new InteractionManager (camera, renderer, scene);
interaction.addClickable (mesh, (intersect ) => {
console .log ("Clicked at:" , intersect.point );
});
Performance Tips
Limit raycasts : Throttle mousemove handlers
Use layers : Filter raycast targets
Simple collision meshes : Use invisible simpler geometry for raycasting
Disable controls when not needed : controls.enabled = false
Batch updates : Group interaction checks
const complexMesh = loadedModel;
const collisionMesh = new THREE .Mesh (
new THREE .BoxGeometry (1 , 1 , 1 ),
new THREE .MeshBasicMaterial ({ visible : false }),
);
collisionMesh.userData .target = complexMesh;
clickables.push (collisionMesh);
See Also
threejs-fundamentals - Camera and scene setup
threejs-animation - Animating interactions
threejs-shaders - Visual feedback effects
Connections
Domain: [[Design & Kreativitaet]]
Kategorie: [[3D & Animation]]
Navigation: [[Skills Uebersicht]], [[Home]]