{"id":"b358ea0b-32d8-4aca-bfa1-35fac56d61ac","shortId":"NQAdHC","kind":"skill","title":"threejs-interaction","tagline":"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.","description":"# Three.js Interaction\n\n## When to Use\n- You need user interaction inside a Three.js scene.\n- The task involves raycasting, object picking, pointer handling, touch input, or camera controls.\n- You are building an interactive 3D experience rather than a passive render.\n\n## Quick Start\n\n```javascript\nimport * as THREE from \"three\";\nimport { OrbitControls } from \"three/addons/controls/OrbitControls.js\";\n\n// Camera controls\nconst controls = new OrbitControls(camera, renderer.domElement);\ncontrols.enableDamping = true;\n\n// Raycasting for click detection\nconst raycaster = new THREE.Raycaster();\nconst mouse = new THREE.Vector2();\n\nfunction onClick(event) {\n  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;\n  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;\n\n  raycaster.setFromCamera(mouse, camera);\n  const intersects = raycaster.intersectObjects(scene.children);\n\n  if (intersects.length > 0) {\n    console.log(\"Clicked:\", intersects[0].object);\n  }\n}\n\nwindow.addEventListener(\"click\", onClick);\n```\n\n## Raycaster\n\n### Basic Raycasting\n\n```javascript\nconst raycaster = new THREE.Raycaster();\n\n// From camera (mouse picking)\nraycaster.setFromCamera(mousePosition, camera);\n\n// From any origin and direction\nraycaster.set(origin, direction); // origin: Vector3, direction: normalized Vector3\n\n// Get intersections\nconst intersects = raycaster.intersectObjects(objects, recursive);\n\n// intersects array contains:\n// {\n//   distance: number,          // Distance from ray origin\n//   point: Vector3,            // Intersection point in world coords\n//   face: Face3,               // Intersected face\n//   faceIndex: number,         // Face index\n//   object: Object3D,          // Intersected object\n//   uv: Vector2,               // UV coordinates at intersection\n//   uv1: Vector2,              // Second UV channel\n//   normal: Vector3,           // Interpolated face normal\n//   instanceId: number         // For InstancedMesh\n// }\n```\n\n### Mouse Position Conversion\n\n```javascript\nconst mouse = new THREE.Vector2();\n\nfunction updateMouse(event) {\n  // For full window\n  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;\n  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;\n}\n\n// For specific canvas element\nfunction updateMouseCanvas(event, canvas) {\n  const rect = canvas.getBoundingClientRect();\n  mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;\n  mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;\n}\n```\n\n### Touch Support\n\n```javascript\nfunction onTouchStart(event) {\n  event.preventDefault();\n\n  if (event.touches.length === 1) {\n    const touch = event.touches[0];\n    mouse.x = (touch.clientX / window.innerWidth) * 2 - 1;\n    mouse.y = -(touch.clientY / window.innerHeight) * 2 + 1;\n\n    raycaster.setFromCamera(mouse, camera);\n    const intersects = raycaster.intersectObjects(clickableObjects);\n\n    if (intersects.length > 0) {\n      handleSelection(intersects[0]);\n    }\n  }\n}\n\nrenderer.domElement.addEventListener(\"touchstart\", onTouchStart);\n```\n\n### Raycaster Options\n\n```javascript\nconst raycaster = new THREE.Raycaster();\n\n// Near/far clipping (default: 0, Infinity)\nraycaster.near = 0;\nraycaster.far = 100;\n\n// Line/Points precision\nraycaster.params.Line.threshold = 0.1;\nraycaster.params.Points.threshold = 0.1;\n\n// Layers (only intersect objects on specific layers)\nraycaster.layers.set(1);\n```\n\n### Efficient Raycasting\n\n```javascript\n// Only check specific objects\nconst clickables = [mesh1, mesh2, mesh3];\nconst intersects = raycaster.intersectObjects(clickables, false);\n\n// Use layers for filtering\nmesh1.layers.set(1); // Clickable layer\nraycaster.layers.set(1);\n\n// Throttle raycast for hover effects\nlet lastRaycast = 0;\nfunction onMouseMove(event) {\n  const now = Date.now();\n  if (now - lastRaycast < 50) return; // 20fps max\n  lastRaycast = now;\n\n  // Raycast here\n}\n```\n\n## Camera Controls\n\n### OrbitControls\n\n```javascript\nimport { OrbitControls } from \"three/addons/controls/OrbitControls.js\";\n\nconst controls = new OrbitControls(camera, renderer.domElement);\n\n// Damping (smooth movement)\ncontrols.enableDamping = true;\ncontrols.dampingFactor = 0.05;\n\n// Rotation limits\ncontrols.minPolarAngle = 0; // Top\ncontrols.maxPolarAngle = Math.PI / 2; // Horizon\ncontrols.minAzimuthAngle = -Math.PI / 4; // Left\ncontrols.maxAzimuthAngle = Math.PI / 4; // Right\n\n// Zoom limits\ncontrols.minDistance = 2;\ncontrols.maxDistance = 50;\n\n// Enable/disable features\ncontrols.enableRotate = true;\ncontrols.enableZoom = true;\ncontrols.enablePan = true;\n\n// Auto-rotate\ncontrols.autoRotate = true;\ncontrols.autoRotateSpeed = 2.0;\n\n// Target (orbit point)\ncontrols.target.set(0, 1, 0);\n\n// Update in animation loop\nfunction animate() {\n  controls.update(); // Required for damping and auto-rotate\n  renderer.render(scene, camera);\n}\n```\n\n#### OrbitControls Programmatic Methods (r183)\n\n```javascript\n// Programmatic camera movement\ncontrols.dolly(1.5); // Dolly in/out (zoom for perspective cameras)\ncontrols.pan(deltaX, deltaY); // Pan the camera\ncontrols.rotate(deltaAzimuth, deltaPolar); // Rotate around target\n\n// Cursor style (r183)\ncontrols.cursorStyle = { orbit: \"grab\", pan: \"move\", dolly: \"zoom-in\" };\n```\n\n### FlyControls\n\n```javascript\nimport { FlyControls } from \"three/addons/controls/FlyControls.js\";\n\nconst controls = new FlyControls(camera, renderer.domElement);\ncontrols.movementSpeed = 10;\ncontrols.rollSpeed = Math.PI / 24;\ncontrols.dragToLook = true;\n\n// Update with delta\nfunction animate() {\n  controls.update(clock.getDelta());\n  renderer.render(scene, camera);\n}\n```\n\n### FirstPersonControls\n\n```javascript\nimport { FirstPersonControls } from \"three/addons/controls/FirstPersonControls.js\";\n\nconst controls = new FirstPersonControls(camera, renderer.domElement);\ncontrols.movementSpeed = 10;\ncontrols.lookSpeed = 0.1;\ncontrols.lookVertical = true;\ncontrols.constrainVertical = true;\ncontrols.verticalMin = Math.PI / 4;\ncontrols.verticalMax = (Math.PI * 3) / 4;\n\nfunction animate() {\n  controls.update(clock.getDelta());\n}\n```\n\n### PointerLockControls\n\n```javascript\nimport { PointerLockControls } from \"three/addons/controls/PointerLockControls.js\";\n\nconst controls = new PointerLockControls(camera, document.body);\n\n// Lock pointer on click\ndocument.addEventListener(\"click\", () => {\n  controls.lock();\n});\n\ncontrols.addEventListener(\"lock\", () => {\n  console.log(\"Pointer locked\");\n});\n\ncontrols.addEventListener(\"unlock\", () => {\n  console.log(\"Pointer unlocked\");\n});\n\n// Movement\nconst velocity = new THREE.Vector3();\nconst direction = new THREE.Vector3();\nconst moveForward = false;\nconst moveBackward = false;\n\ndocument.addEventListener(\"keydown\", (event) => {\n  switch (event.code) {\n    case \"KeyW\":\n      moveForward = true;\n      break;\n    case \"KeyS\":\n      moveBackward = true;\n      break;\n  }\n});\n\nfunction animate() {\n  if (controls.isLocked) {\n    direction.z = Number(moveForward) - Number(moveBackward);\n    direction.normalize();\n\n    velocity.z -= direction.z * 0.1;\n    velocity.z *= 0.9; // Friction\n\n    controls.moveForward(-velocity.z);\n  }\n}\n```\n\n### TrackballControls\n\n```javascript\nimport { TrackballControls } from \"three/addons/controls/TrackballControls.js\";\n\nconst controls = new TrackballControls(camera, renderer.domElement);\ncontrols.rotateSpeed = 2.0;\ncontrols.zoomSpeed = 1.2;\ncontrols.panSpeed = 0.8;\ncontrols.staticMoving = true;\n\nfunction animate() {\n  controls.update();\n}\n```\n\n### MapControls\n\n```javascript\nimport { MapControls } from \"three/addons/controls/MapControls.js\";\n\nconst controls = new MapControls(camera, renderer.domElement);\ncontrols.enableDamping = true;\ncontrols.dampingFactor = 0.05;\ncontrols.screenSpacePanning = false;\ncontrols.maxPolarAngle = Math.PI / 2;\n```\n\n## TransformControls\n\nGizmo for moving/rotating/scaling objects.\n\n```javascript\nimport { TransformControls } from \"three/addons/controls/TransformControls.js\";\n\nconst transformControls = new TransformControls(camera, renderer.domElement);\nscene.add(transformControls);\n\n// Attach to object\ntransformControls.attach(selectedMesh);\n\n// Switch modes\ntransformControls.setMode(\"translate\"); // 'translate', 'rotate', 'scale'\n\n// Change space\ntransformControls.setSpace(\"local\"); // 'local', 'world'\n\n// Size\ntransformControls.setSize(1);\n\n// Events\ntransformControls.addEventListener(\"dragging-changed\", (event) => {\n  // Disable orbit controls while dragging\n  orbitControls.enabled = !event.value;\n});\n\ntransformControls.addEventListener(\"change\", () => {\n  renderer.render(scene, camera);\n});\n\n// Keyboard shortcuts\nwindow.addEventListener(\"keydown\", (event) => {\n  switch (event.key) {\n    case \"g\":\n      transformControls.setMode(\"translate\");\n      break;\n    case \"r\":\n      transformControls.setMode(\"rotate\");\n      break;\n    case \"s\":\n      transformControls.setMode(\"scale\");\n      break;\n    case \"Escape\":\n      transformControls.detach();\n      break;\n  }\n});\n```\n\n## DragControls\n\nDrag objects directly.\n\n```javascript\nimport { DragControls } from \"three/addons/controls/DragControls.js\";\n\nconst draggableObjects = [mesh1, mesh2, mesh3];\nconst dragControls = new DragControls(\n  draggableObjects,\n  camera,\n  renderer.domElement,\n);\n\ndragControls.addEventListener(\"dragstart\", (event) => {\n  orbitControls.enabled = false;\n  event.object.material.emissive.set(0xaaaaaa);\n});\n\ndragControls.addEventListener(\"drag\", (event) => {\n  // Constrain to ground plane\n  event.object.position.y = 0;\n});\n\ndragControls.addEventListener(\"dragend\", (event) => {\n  orbitControls.enabled = true;\n  event.object.material.emissive.set(0x000000);\n});\n```\n\n## Selection System\n\n### Click to Select\n\n```javascript\nconst raycaster = new THREE.Raycaster();\nconst mouse = new THREE.Vector2();\nlet selectedObject = null;\n\nfunction onMouseDown(event) {\n  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;\n  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;\n\n  raycaster.setFromCamera(mouse, camera);\n  const intersects = raycaster.intersectObjects(selectableObjects);\n\n  // Deselect previous\n  if (selectedObject) {\n    selectedObject.material.emissive.set(0x000000);\n  }\n\n  // Select new\n  if (intersects.length > 0) {\n    selectedObject = intersects[0].object;\n    selectedObject.material.emissive.set(0x444444);\n  } else {\n    selectedObject = null;\n  }\n}\n```\n\n### Box Selection\n\n```javascript\nimport { SelectionBox } from \"three/addons/interactive/SelectionBox.js\";\nimport { SelectionHelper } from \"three/addons/interactive/SelectionHelper.js\";\n\nconst selectionBox = new SelectionBox(camera, scene);\nconst selectionHelper = new SelectionHelper(renderer, \"selectBox\"); // CSS class\n\ndocument.addEventListener(\"pointerdown\", (event) => {\n  selectionBox.startPoint.set(\n    (event.clientX / window.innerWidth) * 2 - 1,\n    -(event.clientY / window.innerHeight) * 2 + 1,\n    0.5,\n  );\n});\n\ndocument.addEventListener(\"pointermove\", (event) => {\n  if (selectionHelper.isDown) {\n    selectionBox.endPoint.set(\n      (event.clientX / window.innerWidth) * 2 - 1,\n      -(event.clientY / window.innerHeight) * 2 + 1,\n      0.5,\n    );\n  }\n});\n\ndocument.addEventListener(\"pointerup\", (event) => {\n  selectionBox.endPoint.set(\n    (event.clientX / window.innerWidth) * 2 - 1,\n    -(event.clientY / window.innerHeight) * 2 + 1,\n    0.5,\n  );\n\n  const selected = selectionBox.select();\n  console.log(\"Selected objects:\", selected);\n});\n```\n\n### Hover Effects\n\n```javascript\nconst raycaster = new THREE.Raycaster();\nconst mouse = new THREE.Vector2();\nlet hoveredObject = null;\n\nfunction onMouseMove(event) {\n  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;\n  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;\n\n  raycaster.setFromCamera(mouse, camera);\n  const intersects = raycaster.intersectObjects(hoverableObjects);\n\n  // Reset previous hover\n  if (hoveredObject) {\n    hoveredObject.material.color.set(hoveredObject.userData.originalColor);\n    document.body.style.cursor = \"default\";\n  }\n\n  // Apply new hover\n  if (intersects.length > 0) {\n    hoveredObject = intersects[0].object;\n    if (!hoveredObject.userData.originalColor) {\n      hoveredObject.userData.originalColor =\n        hoveredObject.material.color.getHex();\n    }\n    hoveredObject.material.color.set(0xff6600);\n    document.body.style.cursor = \"pointer\";\n  } else {\n    hoveredObject = null;\n  }\n}\n\nwindow.addEventListener(\"mousemove\", onMouseMove);\n```\n\n## Keyboard Input\n\n```javascript\nconst keys = {};\n\ndocument.addEventListener(\"keydown\", (event) => {\n  keys[event.code] = true;\n});\n\ndocument.addEventListener(\"keyup\", (event) => {\n  keys[event.code] = false;\n});\n\nfunction update() {\n  const speed = 0.1;\n\n  if (keys[\"KeyW\"]) player.position.z -= speed;\n  if (keys[\"KeyS\"]) player.position.z += speed;\n  if (keys[\"KeyA\"]) player.position.x -= speed;\n  if (keys[\"KeyD\"]) player.position.x += speed;\n  if (keys[\"Space\"]) player.position.y += speed;\n  if (keys[\"ShiftLeft\"]) player.position.y -= speed;\n}\n```\n\n## World-Screen Coordinate Conversion\n\n### World to Screen\n\n```javascript\nfunction worldToScreen(position, camera) {\n  const vector = position.clone();\n  vector.project(camera);\n\n  return {\n    x: ((vector.x + 1) / 2) * window.innerWidth,\n    y: (-(vector.y - 1) / 2) * window.innerHeight,\n  };\n}\n\n// Position HTML element over 3D object\nconst screenPos = worldToScreen(mesh.position, camera);\nelement.style.left = screenPos.x + \"px\";\nelement.style.top = screenPos.y + \"px\";\n```\n\n### Screen to World\n\n```javascript\nfunction screenToWorld(screenX, screenY, camera, targetZ = 0) {\n  const vector = new THREE.Vector3(\n    (screenX / window.innerWidth) * 2 - 1,\n    -(screenY / window.innerHeight) * 2 + 1,\n    0.5,\n  );\n\n  vector.unproject(camera);\n\n  const dir = vector.sub(camera.position).normalize();\n  const distance = (targetZ - camera.position.z) / dir.z;\n\n  return camera.position.clone().add(dir.multiplyScalar(distance));\n}\n```\n\n### Ray-Plane Intersection\n\n```javascript\nfunction getRayPlaneIntersection(mouse, camera, plane) {\n  const raycaster = new THREE.Raycaster();\n  raycaster.setFromCamera(mouse, camera);\n\n  const intersection = new THREE.Vector3();\n  raycaster.ray.intersectPlane(plane, intersection);\n\n  return intersection;\n}\n\n// Ground plane\nconst groundPlane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);\nconst worldPos = getRayPlaneIntersection(mouse, camera, groundPlane);\n```\n\n## Event Handling Best Practices\n\n```javascript\nclass InteractionManager {\n  constructor(camera, renderer, scene) {\n    this.camera = camera;\n    this.renderer = renderer;\n    this.scene = scene;\n    this.raycaster = new THREE.Raycaster();\n    this.mouse = new THREE.Vector2();\n    this.clickables = [];\n\n    this.bindEvents();\n  }\n\n  bindEvents() {\n    const canvas = this.renderer.domElement;\n\n    canvas.addEventListener(\"click\", (e) => this.onClick(e));\n    canvas.addEventListener(\"mousemove\", (e) => this.onMouseMove(e));\n    canvas.addEventListener(\"touchstart\", (e) => this.onTouchStart(e));\n  }\n\n  updateMouse(event) {\n    const rect = this.renderer.domElement.getBoundingClientRect();\n    this.mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;\n    this.mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;\n  }\n\n  getIntersects() {\n    this.raycaster.setFromCamera(this.mouse, this.camera);\n    return this.raycaster.intersectObjects(this.clickables, true);\n  }\n\n  onClick(event) {\n    this.updateMouse(event);\n    const intersects = this.getIntersects();\n\n    if (intersects.length > 0) {\n      const object = intersects[0].object;\n      if (object.userData.onClick) {\n        object.userData.onClick(intersects[0]);\n      }\n    }\n  }\n\n  addClickable(object, callback) {\n    this.clickables.push(object);\n    object.userData.onClick = callback;\n  }\n\n  dispose() {\n    // Remove event listeners\n  }\n}\n\n// Usage\nconst interaction = new InteractionManager(camera, renderer, scene);\ninteraction.addClickable(mesh, (intersect) => {\n  console.log(\"Clicked at:\", intersect.point);\n});\n```\n\n## Performance Tips\n\n1. **Limit raycasts**: Throttle mousemove handlers\n2. **Use layers**: Filter raycast targets\n3. **Simple collision meshes**: Use invisible simpler geometry for raycasting\n4. **Disable controls when not needed**: `controls.enabled = false`\n5. **Batch updates**: Group interaction checks\n\n```javascript\n// Use simpler geometry for raycasting\nconst complexMesh = loadedModel;\nconst collisionMesh = new THREE.Mesh(\n  new THREE.BoxGeometry(1, 1, 1),\n  new THREE.MeshBasicMaterial({ visible: false }),\n);\ncollisionMesh.userData.target = complexMesh;\nclickables.push(collisionMesh);\n```\n\n## See Also\n\n- `threejs-fundamentals` - Camera and scene setup\n- `threejs-animation` - Animating interactions\n- `threejs-shaders` - Visual feedback effects\n\n## Limitations\n- Use this skill only when the task clearly matches the scope described above.\n- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.\n- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.","tags":["threejs","interaction","antigravity","awesome","skills","sickn33","agent-skills","agentic-skills","ai-agent-skills","ai-agents","ai-coding","ai-workflows"],"capabilities":["skill","source-sickn33","skill-threejs-interaction","topic-agent-skills","topic-agentic-skills","topic-ai-agent-skills","topic-ai-agents","topic-ai-coding","topic-ai-workflows","topic-antigravity","topic-antigravity-skills","topic-claude-code","topic-claude-code-skills","topic-codex-cli","topic-codex-skills"],"categories":["antigravity-awesome-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/sickn33/antigravity-awesome-skills/threejs-interaction","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add sickn33/antigravity-awesome-skills","source_repo":"https://github.com/sickn33/antigravity-awesome-skills","install_from":"skills.sh"}},"qualityScore":"0.700","qualityRationale":"deterministic score 0.70 from registry signals: · indexed on github topic:agent-skills · 34460 github stars · SKILL.md body (17,007 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-04-22T06:52:01.354Z","embedding":null,"createdAt":"2026-04-18T21:46:18.377Z","updatedAt":"2026-04-22T06:52:01.354Z","lastSeenAt":"2026-04-22T06:52:01.354Z","tsv":"'0':122,126,274,294,297,311,314,366,408,447,449,808,863,866,994,997,1127,1193,1195,1196,1283,1287,1293 '0.05':404,682 '0.1':320,322,551,638,1034 '0.5':910,925,938,1140 '0.8':661 '0.9':640 '0x000000':815,858 '0x444444':869 '0xaaaaaa':798 '0xff6600':1004 '1':107,112,232,237,254,260,270,279,284,331,354,358,448,726,840,845,905,909,920,924,933,937,967,972,1092,1097,1135,1139,1194,1258,1265,1322,1373,1374,1375 '1.2':659 '1.5':476 '10':520,549 '100':316 '2':106,111,231,236,253,259,278,283,412,425,687,839,844,904,908,919,923,932,936,966,971,1093,1098,1134,1138,1257,1264,1328 '2.0':442,657 '20fps':378 '24':523 '3':561,1334 '3d':26,59,1104 '4':416,420,558,562,1344 '5':1352 '50':376,427 'ad':20 'add':1156 'addclick':1294 'also':1385 'anim':452,455,530,564,627,665,1395,1396 'appli':989 'around':493 'array':167 'ask':1437 'attach':706 'auto':437,462 'auto-rot':436,461 'basic':132 'batch':1353 'best':1205 'bindev':1228 'boundari':1445 'box':873 'break':620,625,756,761,766,770 'build':56 'callback':1296,1300 'camera':21,52,78,84,115,140,145,287,384,396,466,473,482,488,517,535,546,577,654,677,702,744,790,848,888,975,1083,1088,1110,1125,1142,1167,1175,1201,1211,1215,1310,1389 'camera.position':1146,1151 'camera.position.clone':1155 'canva':240,245,1230 'canvas.addeventlistener':1232,1237,1242 'canvas.getboundingclientrect':248 'case':616,621,752,757,762,767 'chang':718,731,741 'channel':204 'check':336,1357 'clarif':1439 'class':897,1208 'clear':1412 'click':18,90,124,129,582,584,818,1233,1317 'clickabl':340,347,355 'clickableobject':291 'clickables.push':1382 'clip':309 'clock.getdelta':532,566 'collis':1336 'collisionmesh':1368,1383 'collisionmesh.userdata.target':1380 'complexmesh':1365,1381 'console.log':123,588,593,942,1316 'const':80,92,96,116,135,161,218,246,271,288,304,339,344,370,392,513,542,573,597,601,605,608,650,673,698,780,785,822,826,849,884,890,939,949,953,976,1016,1032,1084,1106,1128,1143,1148,1169,1176,1187,1197,1229,1249,1278,1284,1306,1364,1367 'constrain':802 'constructor':1210 'contain':168 'control':7,22,53,79,81,385,393,514,543,574,651,674,735,1346 'controls.addeventlistener':586,591 'controls.autorotate':439 'controls.autorotatespeed':441 'controls.constrainvertical':554 'controls.cursorstyle':498 'controls.dampingfactor':403,681 'controls.dolly':475 'controls.dragtolook':524 'controls.enabled':1350 'controls.enabledamping':86,401,679 'controls.enablepan':434 'controls.enablerotate':430 'controls.enablezoom':432 'controls.islocked':629 'controls.lock':585 'controls.lookspeed':550 'controls.lookvertical':552 'controls.maxazimuthangle':418 'controls.maxdistance':426 'controls.maxpolarangle':410,685 'controls.minazimuthangle':414 'controls.mindistance':424 'controls.minpolarangle':407 'controls.moveforward':642 'controls.movementspeed':519,548 'controls.pan':483 'controls.panspeed':660 'controls.rollspeed':521 'controls.rotate':489 'controls.rotatespeed':656 'controls.screenspacepanning':683 'controls.staticmoving':662 'controls.target.set':446 'controls.update':456,531,565,666 'controls.verticalmax':559 'controls.verticalmin':556 'controls.zoomspeed':658 'convers':216,1075 'coord':181 'coordin':197,1074 'creat':24 'criteria':1448 'css':896 'cursor':495 'damp':398,459 'date.now':372 'default':310,988 'delta':528 'deltaazimuth':490 'deltapolar':491 'deltax':484 'deltay':485 'describ':1416 'deselect':853 'detect':19,91 'dir':1144 'dir.multiplyscalar':1157 'dir.z':1153 'direct':150,153,156,602,774 'direction.normalize':635 'direction.z':630,637 'disabl':733,1345 'dispos':1301 'distanc':169,171,1149,1158 'document.addeventlistener':583,611,898,911,926,1018,1024 'document.body':578 'document.body.style.cursor':987,1005 'dolli':477,503 'drag':730,737,772,800 'dragcontrol':771,777,786,788 'dragcontrols.addeventlistener':792,799,809 'dragend':810 'draggableobject':781,789 'dragging-chang':729 'dragstart':793 'e':1234,1236,1239,1241,1244,1246 'effect':363,947,1403 'effici':332 'element':241,1102 'element.style.left':1111 'element.style.top':1114 'els':870,1007 'enable/disable':428 'environ':1428 'environment-specif':1427 'escap':768 'event':102,224,244,266,369,613,727,732,749,794,801,811,835,900,913,928,962,1020,1026,1203,1248,1275,1277,1303 'event.clientx':104,229,250,837,902,917,930,964,1254 'event.clienty':109,234,256,842,906,921,934,969,1261 'event.code':615,1022,1028 'event.key':751 'event.object.material.emissive.set':797,814 'event.object.position':806 'event.preventdefault':267 'event.touches':273 'event.touches.length':269 'event.value':739 'experi':27,60 'expert':1433 'face':182,185,188,208 'face3':183 'faceindex':186 'fals':348,607,610,684,796,1029,1351,1379 'featur':429 'feedback':1402 'filter':352,1331 'firstpersoncontrol':536,539,545 'flycontrol':507,510,516 'friction':641 'full':226 'function':100,222,242,264,367,454,529,563,626,664,833,960,1030,1080,1121,1164 'fundament':1388 'g':753 'geometri':1341,1361 'get':159 'getintersect':1266 'getrayplaneintersect':1165,1199 'gizmo':689 'grab':500 'ground':804,1185 'groundplan':1188,1202 'group':1355 'handl':14,48,1204 'handler':1327 'handleselect':295 'horizon':413 'hover':362,946,982,991 'hoverableobject':979 'hoveredobject':958,984,995,1008 'hoveredobject.material.color.gethex':1002 'hoveredobject.material.color.set':985,1003 'hoveredobject.userdata.originalcolor':986,1000,1001 'html':1101 'implement':17 'import':69,74,388,509,538,569,646,669,694,776,876,880 'in/out':478 'index':189 'infin':312 'input':9,16,50,1014,1442 'insid':37 'instancedmesh':213 'instanceid':210 'interact':3,5,25,29,36,58,1307,1356,1397 'interaction.addclickable':1313 'interactionmanag':1209,1309 'interpol':207 'intersect':117,125,160,162,166,177,184,192,199,289,296,325,345,850,865,977,996,1162,1177,1182,1184,1279,1286,1292,1315 'intersect.point':1319 'intersects.length':121,293,862,993,1282 'invis':1339 'involv':43 'javascript':68,134,217,263,303,334,387,471,508,537,568,645,668,693,775,821,875,948,1015,1079,1120,1163,1207,1358 'key':622,1017,1021,1027,1036,1042,1043,1048,1054,1060,1066 'keya':1049 'keyboard':745,1013 'keyd':1055 'keydown':612,748,1019 'keyup':1025 'keyw':617,1037 'lastraycast':365,375,380 'layer':323,329,350,356,1330 'left':417 'let':364,830,957 'limit':406,423,1323,1404 'line/points':317 'listen':1304 'loadedmodel':1366 'local':721,722 'lock':579,587,590 'loop':453 'mapcontrol':667,670,676 'match':1413 'math.pi':411,415,419,522,557,560,686 'max':379 'mesh':1314,1337 'mesh.position':1109 'mesh1':341,782 'mesh1.layers.set':353 'mesh2':342,783 'mesh3':343,784 'method':469 'miss':1450 'mode':712 'mous':97,114,141,214,219,286,827,847,954,974,1166,1174,1200 'mouse.x':103,228,249,275,836,963 'mouse.y':108,233,255,280,841,968 'mouse/touch':8 'mousemov':1011,1238,1326 'mouseposit':144 'move':502 'movebackward':609,623,634 'moveforward':606,618,632 'movement':400,474,596 'moving/rotating/scaling':691 'near/far':308 'need':34,1349 'new':82,94,98,137,220,306,394,515,544,575,599,603,652,675,700,787,824,828,860,886,892,951,955,990,1130,1171,1178,1189,1191,1221,1224,1308,1369,1371,1376 'normal':157,205,209,1147 'null':832,872,959,1009 'number':170,187,211,631,633 'object':10,45,127,164,190,193,326,338,692,708,773,867,944,998,1105,1285,1288,1295,1298 'object.userdata.onclick':1290,1291,1299 'object3d':191 'onclick':101,130,1274 'onmousedown':834 'onmousemov':368,961,1012 'ontouchstart':265,300 'option':302 'orbit':444,499,734 'orbitcontrol':75,83,386,389,395,467 'orbitcontrols.enabled':738,795,812 'origin':148,152,154,174 'output':1422 'pan':486,501 'passiv':64 'perform':1320 'permiss':1443 'perspect':481 'pick':46,142 'plane':805,1161,1168,1181,1186 'player.position':1038,1044,1050,1056,1062,1068 'point':175,178,445 'pointer':47,580,589,594,1006 'pointerdown':899 'pointerlockcontrol':567,570,576 'pointermov':912 'pointerup':927 'posit':215,1082,1100 'position.clone':1086 'practic':1206 'precis':318 'previous':854,981 'programmat':468,472 'px':1113,1116 'quick':66 'r':758 'r183':470,497 'rather':61 'ray':173,1160 'ray-plan':1159 'raycast':6,44,88,93,131,133,136,301,305,333,360,382,823,950,1170,1324,1332,1343,1363 'raycaster.far':315 'raycaster.intersectobjects':118,163,290,346,851,978 'raycaster.layers.set':330,357 'raycaster.near':313 'raycaster.params.line.threshold':319 'raycaster.params.points.threshold':321 'raycaster.ray.intersectplane':1180 'raycaster.set':151 'raycaster.setfromcamera':113,143,285,846,973,1173 'rect':247,1250 'rect.height':258,1263 'rect.left':251,1255 'rect.top':257,1262 'rect.width':252,1256 'recurs':165 'remov':1302 'render':65,894,1212,1217,1311 'renderer.domelement':85,397,518,547,655,678,703,791 'renderer.domelement.addeventlistener':298 'renderer.render':464,533,742 'requir':457,1441 'reset':980 'return':377,1089,1154,1183,1270 'review':1434 'right':421 'rotat':405,438,463,492,716,760 'safeti':1444 'scale':717,765 'scene':40,465,534,743,889,1213,1219,1312,1391 'scene.add':704 'scene.children':119 'scope':1415 'screen':1073,1078,1117 'screeni':1124,1136 'screenpo':1107 'screenpos.x':1112 'screenpos.y':1115 'screentoworld':1122 'screenx':1123,1132 'second':202 'see':1384 'select':11,816,820,859,874,940,943,945 'selectableobject':852 'selectbox':895 'selectedmesh':710 'selectedobject':831,856,864,871 'selectedobject.material.emissive.set':857,868 'selectionbox':877,885,887 'selectionbox.endpoint.set':916,929 'selectionbox.select':941 'selectionbox.startpoint.set':901 'selectionhelp':881,891,893 'selectionhelper.isdown':915 'setup':1392 'shader':1400 'shiftleft':1067 'shortcut':746 'simpl':1335 'simpler':1340,1360 'size':724 'skill':1407 'skill-threejs-interaction' 'smooth':399 'source-sickn33' 'space':719,1061 'specif':239,328,337,1429 'speed':1033,1040,1046,1052,1058,1064,1070 'start':67 'stop':1435 'style':496 'substitut':1425 'success':1447 'support':262 'switch':614,711,750 'system':817 'target':443,494,1333 'targetz':1126,1150 'task':42,1411 'test':1431 'this.bindevents':1227 'this.camera':1214,1269 'this.clickables':1226,1272 'this.clickables.push':1297 'this.getintersects':1280 'this.mouse':1223,1252,1259,1268 'this.onclick':1235 'this.onmousemove':1240 'this.ontouchstart':1245 'this.raycaster':1220 'this.raycaster.intersectobjects':1271 'this.raycaster.setfromcamera':1267 'this.renderer':1216 'this.renderer.domelement':1231 'this.renderer.domelement.getboundingclientrect':1251 'this.scene':1218 'this.updatemouse':1276 'three':71,73 'three.boxgeometry':1372 'three.js':4,28,39 'three.mesh':1370 'three.meshbasicmaterial':1377 'three.plane':1190 'three.raycaster':95,138,307,825,952,1172,1222 'three.vector2':99,221,829,956,1225 'three.vector3':600,604,1131,1179,1192 'three/addons/controls/dragcontrols.js':779 'three/addons/controls/firstpersoncontrols.js':541 'three/addons/controls/flycontrols.js':512 'three/addons/controls/mapcontrols.js':672 'three/addons/controls/orbitcontrols.js':77,391 'three/addons/controls/pointerlockcontrols.js':572 'three/addons/controls/trackballcontrols.js':649 'three/addons/controls/transformcontrols.js':697 'three/addons/interactive/selectionbox.js':879 'three/addons/interactive/selectionhelper.js':883 'threej':2,1387,1394,1399 'threejs-anim':1393 'threejs-fundament':1386 'threejs-interact':1 'threejs-shad':1398 'throttl':359,1325 'tip':1321 'top':409 'topic-agent-skills' 'topic-agentic-skills' 'topic-ai-agent-skills' 'topic-ai-agents' 'topic-ai-coding' 'topic-ai-workflows' 'topic-antigravity' 'topic-antigravity-skills' 'topic-claude-code' 'topic-claude-code-skills' 'topic-codex-cli' 'topic-codex-skills' 'touch':49,261,272 'touch.clientx':276 'touch.clienty':281 'touchstart':299,1243 'trackballcontrol':644,647,653 'transformcontrol':688,695,699,701,705 'transformcontrols.addeventlistener':728,740 'transformcontrols.attach':709 'transformcontrols.detach':769 'transformcontrols.setmode':713,754,759,764 'transformcontrols.setsize':725 'transformcontrols.setspace':720 'translat':714,715,755 'treat':1420 'true':87,402,431,433,435,440,525,553,555,619,624,663,680,813,1023,1273 'unlock':592,595 'updat':450,526,1031,1354 'updatemous':223,1247 'updatemousecanva':243 'usag':1305 'use':12,32,349,1329,1338,1359,1405 'user':15,35 'uv':194,196,203 'uv1':200 'valid':1430 'vector':1085,1129 'vector.project':1087 'vector.sub':1145 'vector.unproject':1141 'vector.x':1091 'vector.y':1096 'vector2':195,201 'vector3':155,158,176,206 'veloc':598 'velocity.z':636,639,643 'visibl':1378 'visual':1401 'window':227 'window.addeventlistener':128,747,1010 'window.innerheight':110,235,282,843,907,922,935,970,1099,1137 'window.innerwidth':105,230,277,838,903,918,931,965,1094,1133 'world':180,723,1072,1076,1119 'world-screen':1071 'worldpo':1198 'worldtoscreen':1081,1108 'x':1051,1057,1090,1253 'y':807,1063,1069,1095,1260 'z':1039,1045,1152 'zoom':422,479,505 'zoom-in':504","prices":[{"id":"21e3b8c8-1235-474b-bd24-b18225f9dd94","listingId":"b358ea0b-32d8-4aca-bfa1-35fac56d61ac","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"sickn33","category":"antigravity-awesome-skills","install_from":"skills.sh"},"createdAt":"2026-04-18T21:46:18.377Z"}],"sources":[{"listingId":"b358ea0b-32d8-4aca-bfa1-35fac56d61ac","source":"github","sourceId":"sickn33/antigravity-awesome-skills/threejs-interaction","sourceUrl":"https://github.com/sickn33/antigravity-awesome-skills/tree/main/skills/threejs-interaction","isPrimary":false,"firstSeenAt":"2026-04-18T21:46:18.377Z","lastSeenAt":"2026-04-22T06:52:01.354Z"}],"details":{"listingId":"b358ea0b-32d8-4aca-bfa1-35fac56d61ac","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"sickn33","slug":"threejs-interaction","github":{"repo":"sickn33/antigravity-awesome-skills","stars":34460,"topics":["agent-skills","agentic-skills","ai-agent-skills","ai-agents","ai-coding","ai-workflows","antigravity","antigravity-skills","claude-code","claude-code-skills","codex-cli","codex-skills","cursor","cursor-skills","developer-tools","gemini-cli","gemini-skills","kiro","mcp","skill-library"],"license":"mit","html_url":"https://github.com/sickn33/antigravity-awesome-skills","pushed_at":"2026-04-22T06:40:00Z","description":"Installable GitHub library of 1,400+ agentic skills for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and more. Includes installer CLI, bundles, workflows, and official/community skill collections.","skill_md_sha":"1853087ffaca747b93197cb17c99760ef3e26237","skill_md_path":"skills/threejs-interaction/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/sickn33/antigravity-awesome-skills/tree/main/skills/threejs-interaction"},"layout":"multi","source":"github","category":"antigravity-awesome-skills","frontmatter":{"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."},"skills_sh_url":"https://skills.sh/sickn33/antigravity-awesome-skills/threejs-interaction"},"updatedAt":"2026-04-22T06:52:01.354Z"}}