[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M71K33FDXH8NTPRFJPS4JTRP":3,"skill-same:01M71K33FDXH8NTPRFJPS4JTRP":76},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":44,"linkOnly":45,"markdownContent":46,"markdownBody":47,"frontMatter":48,"refName":55,"commitSha":56,"skillDirectory":57,"forksCount":58,"repositoryHtmlUrl":59,"repositoryHomepageUrl":6,"ownerHtmlUrl":60,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":61,"install":62,"resources":75},"01M71K33FDXH8NTPRFJPS4JTRP","3d-scene",null,"Use when building 3D scenes for web — Three.js, React Three Fiber (R3F), Spline embeds, GLTF\u002FGLB loading, scroll-linked 3D, camera animations. Covers scene setup, model optimization, and performance budgets.","KeiSeiLab\u002FKeiSeiKit","KeiSeiLab","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F283315582?v=4","01M71K2EW81JRFHSGF0T844B32","KeiSei Lab","01M71K2EWXTB52KWC65E55S6M2",7,"Apache-2.0","https:\u002F\u002Fgithub.com\u002FKeiSeiLab\u002FKeiSeiKit\u002Fblob\u002Fmain\u002Fskills\u002F3d-scene\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43],"3d scene","3D","three","hover","camera","orbit","spline","interaction","react","when","false","auto","auto-detect","default","performance","static","loading","model","react three fiber","glb","2026-11-10 10:12:30",false,"---\nname: 3d-scene\ndescription: Use when building 3D scenes for web — Three.js, React Three Fiber (R3F), Spline embeds, GLTF\u002FGLB loading, scroll-linked 3D, camera animations. Covers scene setup, model optimization, and performance budgets.\narguments:\n  - name: approach\n    description: \"Approach: r3f, threejs-vanilla, spline-embed (auto-detect if omitted)\"\n    required: false\n  - name: interaction\n    description: \"Interaction: scroll-linked, orbit, hover, static (default: scroll-linked)\"\n    required: false\n---\n\n# 3D Scene Skill\n\n## Decision Matrix — Pick Approach\n\n| Need | Approach | Bundle Impact | Complexity |\n|------|----------|---------------|------------|\n| Product showcase with scroll | R3F + ScrollControls | ~150KB (Three.js) | Medium |\n| Hero 3D scene (no-code) | Spline embed | ~0KB (iframe) | Low |\n| Custom shaders + 3D | Three.js vanilla | ~150KB | High |\n| Lightweight shader-only | OGL | ~8KB | Medium |\n| Interactive configurator | R3F + Leva\u002Fdat.gui | ~150KB | Medium |\n\n---\n\n## 1. React Three Fiber (R3F) + Drei [E1]\n\n**Install:**\n```bash\nnpm i three @react-three\u002Ffiber @react-three\u002Fdrei\n```\n\n### Basic Scene\n\n```jsx\nimport { Canvas } from \"@react-three\u002Ffiber\";\nimport { OrbitControls, Environment, useGLTF } from \"@react-three\u002Fdrei\";\n\nfunction Model({ url }) {\n  const { scene } = useGLTF(url);\n  return \u003Cprimitive object={scene} \u002F>;\n}\n\nfunction Scene() {\n  return (\n    \u003CCanvas\n      camera={{ position: [0, 2, 5], fov: 45 }}\n      style={{ width: \"100%\", height: \"100vh\" }}\n    >\n      \u003CEnvironment preset=\"studio\" \u002F>\n      \u003CModel url=\"\u002Fmodels\u002Fproduct.glb\" \u002F>\n      \u003COrbitControls enableZoom={false} \u002F>\n    \u003C\u002FCanvas>\n  );\n}\n```\n\n### ScrollControls (Scroll-Linked 3D) [E1]\n\n```jsx\nimport { Canvas, useFrame } from \"@react-three\u002Ffiber\";\nimport { ScrollControls, Scroll, useScroll } from \"@react-three\u002Fdrei\";\n\nfunction AnimatedModel() {\n  const scroll = useScroll();\n  const ref = useRef();\n\n  useFrame(() => {\n    const offset = scroll.offset; \u002F\u002F 0 to 1\n    \u002F\u002F Rotate model based on scroll\n    ref.current.rotation.y = offset * Math.PI * 2;\n    \u002F\u002F Move camera\u002Fmodel along a path\n    ref.current.position.y = offset * -5;\n  });\n\n  return (\n    \u003Cmesh ref={ref}>\n      \u003CboxGeometry args={[1, 1, 1]} \u002F>\n      \u003CmeshStandardMaterial color=\"royalblue\" \u002F>\n    \u003C\u002Fmesh>\n  );\n}\n\nfunction ScrollScene() {\n  return (\n    \u003CCanvas>\n      \u003CScrollControls\n        pages={5}       \u002F\u002F 5 pages of scroll (500vh)\n        damping={0.25}  \u002F\u002F friction (seconds to catch up)\n      >\n        {\u002F* 3D content *\u002F}\n        \u003CScroll>\n          \u003CAnimatedModel \u002F>\n        \u003C\u002FScroll>\n\n        {\u002F* HTML content overlaid *\u002F}\n        \u003CScroll html>\n          \u003Cdiv style={{ position: \"absolute\", top: \"100vh\" }}>\n            \u003Ch2>Section 2\u003C\u002Fh2>\n          \u003C\u002Fdiv>\n          \u003Cdiv style={{ position: \"absolute\", top: \"200vh\" }}>\n            \u003Ch2>Section 3\u003C\u002Fh2>\n          \u003C\u002Fdiv>\n        \u003C\u002FScroll>\n      \u003C\u002FScrollControls>\n\n      \u003CambientLight intensity={0.5} \u002F>\n      \u003CdirectionalLight position={[5, 5, 5]} \u002F>\n    \u003C\u002FCanvas>\n  );\n}\n```\n\n### useScroll Utilities\n\n```jsx\nconst scroll = useScroll();\n\n\u002F\u002F scroll.offset     — 0 to 1 (overall progress)\n\u002F\u002F scroll.delta      — scroll speed\n\u002F\u002F scroll.range(from, distance)  — 0-1 within range\n\u002F\u002F scroll.curve(from, distance)  — bell curve within range\n\u002F\u002F scroll.visible(from, distance) — boolean visibility\n\nuseFrame(() => {\n  \u002F\u002F Animate only in section 2 (20%-40% of scroll)\n  const sectionProgress = scroll.range(0.2, 0.2);\n  ref.current.scale.setScalar(1 + sectionProgress * 0.5);\n\n  \u002F\u002F Fade in section 3\n  const visible = scroll.visible(0.4, 0.2);\n  material.current.opacity = visible ? scroll.curve(0.4, 0.2) : 0;\n});\n```\n\n### R3F + GSAP (Alternative to ScrollControls) [E2]\n\nWhen you need GSAP's pin\u002Fsnap with 3D:\n\n```jsx\nimport { useGSAP } from \"@gsap\u002Freact\";\nimport gsap from \"gsap\";\nimport { ScrollTrigger } from \"gsap\u002FScrollTrigger\";\n\nfunction GSAPModel() {\n  const mesh = useRef();\n\n  useGSAP(() => {\n    gsap.to(mesh.current.rotation, {\n      y: Math.PI * 2,\n      scrollTrigger: {\n        trigger: \"#scene-container\",\n        start: \"top top\",\n        end: \"+=3000\",\n        pin: true,\n        scrub: 1,\n      }\n    });\n  });\n\n  return \u003Cmesh ref={mesh}>...\u003C\u002Fmesh>;\n}\n```\n\n---\n\n## 2. Spline Embeds [E2]\n\n**What:** No-code 3D design tool with web export.\n**Best for:** Quick 3D heroes, interactive product views, landing pages.\n\n### Embed Methods\n\n```jsx\n\u002F\u002F Method 1: iframe (simplest, no bundle impact)\n\u003Ciframe\n  src=\"https:\u002F\u002Fmy.spline.design\u002Fscene-abc123\u002F\"\n  frameBorder=\"0\"\n  width=\"100%\"\n  height=\"500px\"\n  style={{ border: \"none\" }}\n\u002F>\n\n\u002F\u002F Method 2: React component (more control)\nimport Spline from \"@splinetool\u002Freact-spline\";\n\n\u003CSpline scene=\"https:\u002F\u002Fprod.spline.design\u002Fabc123\u002Fscene.splinecode\" \u002F>\n\n\u002F\u002F Method 3: Vanilla JS\nimport { Application } from \"@splinetool\u002Fruntime\";\nconst app = new Application(canvas);\napp.load(\"https:\u002F\u002Fprod.spline.design\u002Fabc123\u002Fscene.splinecode\");\n```\n\n### Spline Capabilities\n\n- Scroll-linked animations (native events)\n- Mouse follow \u002F hover interactions\n- State changes on click\n- Physics simulations\n- Responsive layout\n- AI text-to-3D \u002F image-to-3D generation\n\n### Spline Limitations\n\n- **Performance:** Keep \u003C3 lights per scene\n- **Polygons:** Smooth subdivision max 2 levels\n- **Loading:** Scenes can be 1-5MB+ for complex objects\n- **Control:** Less fine-grained than custom Three.js\n- **Offline:** Requires network to load from Spline CDN (self-hosted export available)\n- **Scroll sync:** Less precise than R3F ScrollControls or GSAP\n\n### Spline Optimization\n\n- Delete invisible objects (inside or behind other objects)\n- Use \u003C3 lights\n- Compress on export (quality vs size tradeoff)\n- Reduce subdivision levels\n- Optimize CAD imports: strip internal geometry, small fillets\n- Keep total polygon count reasonable for target devices\n\n---\n\n## 3. GLTF\u002FGLB Loading & Optimization [E1]\n\n### The Pipeline\n\n```\nSource (FBX\u002FOBJ\u002FBlender)\n  → Export as GLTF\u002FGLB\n  → Optimize with gltf-transform\n    → Draco\u002FMeshopt compression\n    → KTX2 texture compression\n    → Mesh quantization\n  → Load in Three.js\u002FR3F\n```\n\n### gltf-transform Optimization [E1]\n\n```bash\n# Install\nnpm i -g @gltf-transform\u002Fcli\n\n# Full optimization pipeline\ngltf-transform optimize input.glb output.glb \\\n  --compress draco \\\n  --texture-compress webp\n\n# Or step by step:\ngltf-transform dedup input.glb deduped.glb           # remove duplicate data\ngltf-transform draco deduped.glb compressed.glb       # geometry compression\ngltf-transform webp compressed.glb textured.glb       # texture to WebP\ngltf-transform quantize textured.glb output.glb       # mesh quantization\n```\n\n### Real-World Size Reductions\n\n| Stage | Example Size | Reduction |\n|-------|-------------|-----------|\n| Raw FBX | 50 MB | baseline |\n| GLTF\u002FGLB export | 29 MB | -42% |\n| Draco compression | 5 MB | -83% |\n| + KTX2 textures | 2.5 MB | -91% |\n| + Mesh quantization | 2 MB | -93% |\n\n### Compression Methods\n\n| Method | Compression | Decode Speed | Three.js Version |\n|--------|-------------|--------------|-------------------|\n| Draco | Best (~90%) | Slower (Web Worker) | r100+ |\n| Meshopt | Good (~85%) | Faster | r122+ |\n| Quantization | Moderate (~50%) | Instant | r100+ |\n\n**Decision:** Draco for smallest files, Meshopt for fastest client decode. Combine with KTX2 textures.\n\n### Loading in R3F\n\n```jsx\nimport { useGLTF, useTexture } from \"@react-three\u002Fdrei\";\n\n\u002F\u002F Preload for instant display\nuseGLTF.preload(\"\u002Fmodels\u002Fproduct.glb\");\n\nfunction Product() {\n  const { scene, nodes, materials } = useGLTF(\"\u002Fmodels\u002Fproduct.glb\");\n\n  return (\n    \u003Cprimitive\n      object={scene}\n      scale={0.5}\n      position={[0, -1, 0]}\n    \u002F>\n  );\n}\n\n\u002F\u002F With Draco decoder\nimport { DRACOLoader } from \"three\u002Fexamples\u002Fjsm\u002Floaders\u002FDRACOLoader\";\n\n\u002F\u002F In Canvas setup or loader config:\n\u002F\u002F DRACOLoader points to decoder files (usually from CDN)\n```\n\n---\n\n## 4. Performance Budgets [E1]\n\n### Polygon Limits\n\n| Device Class | Max Triangles | Draw Calls | Notes |\n|-------------|---------------|------------|-------|\n| High-end desktop | 500K-1M | \u003C200 | Gaming GPU |\n| Average desktop | 100K-300K | \u003C100 | Integrated GPU |\n| Mobile (flagship) | 50K-150K | \u003C50 | iPhone 14+ level |\n| Mobile (budget) | 20K-50K | \u003C30 | Older Android |\n\n**Key insight:** Draw call count matters MORE than polygon count. Below 100 draw calls, most devices maintain 60fps. Above 500, even powerful GPUs struggle.\n\n### Texture Budgets\n\n| Texture | Max Size | Format | VRAM |\n|---------|----------|--------|------|\n| Diffuse\u002FAlbedo | 2048x2048 | KTX2\u002FWebP | ~5MB |\n| Normal map | 1024x1024 | KTX2 | ~1.3MB |\n| Roughness\u002FMetal | 512x512 | KTX2 | ~0.3MB |\n| Environment | 256x256 cube | HDR\u002FEXR | ~2MB |\n\n**Critical:** A 200KB PNG on disk = 20MB+ in VRAM! KTX2 with Basis Universal stays compressed on GPU (~10x reduction).\n\n### R3F Performance Tips\n\n```jsx\n\u002F\u002F 1. Use instancing for repeated objects\nimport { Instances, Instance } from \"@react-three\u002Fdrei\";\n\n\u003CInstances>\n  {positions.map((pos, i) => (\n    \u003CInstance key={i} position={pos} \u002F>\n  ))}\n\u003C\u002FInstances>\n\n\u002F\u002F 2. Frustum culling (enabled by default in Three.js)\n\u002F\u002F 3. LOD (Level of Detail)\nimport { Detailed } from \"@react-three\u002Fdrei\";\n\n\u003CDetailed distances={[0, 50, 100]}>\n  \u003CHighPolyModel \u002F>\n  \u003CMedPolyModel \u002F>\n  \u003CLowPolyModel \u002F>\n\u003C\u002FDetailed>\n\n\u002F\u002F 4. Limit pixel ratio on mobile\n\u003CCanvas dpr={[1, 2]}> {\u002F* max 2x, not device native *\u002F}\n\n\u002F\u002F 5. Use drei's Preload for loading screen\nimport { Preload } from \"@react-three\u002Fdrei\";\n\u003CCanvas>\n  \u003CSuspense fallback={null}>\n    \u003CScene \u002F>\n  \u003C\u002FSuspense>\n  \u003CPreload all \u002F>\n\u003C\u002FCanvas>\n```\n\n### Monitor Performance\n\n```jsx\nimport { useFrame } from \"@react-three\u002Ffiber\";\nimport { Stats } from \"@react-three\u002Fdrei\";\n\n\u002F\u002F Show FPS\u002FMS\u002FMB overlay\n\u003CStats \u002F>\n\n\u002F\u002F Or manual monitoring\nuseFrame((state) => {\n  const info = state.gl.info;\n  \u002F\u002F info.render.triangles — triangles per frame\n  \u002F\u002F info.render.calls — draw calls per frame\n  \u002F\u002F info.memory.textures — loaded textures\n  \u002F\u002F info.memory.geometries — loaded geometries\n});\n```\n\n---\n\n## 5. Lighting & Environment [E2]\n\n### Quick Setups (drei)\n\n```jsx\n\u002F\u002F Studio lighting (product showcase)\n\u003CEnvironment preset=\"studio\" \u002F>\n\n\u002F\u002F HDR environment\n\u003CEnvironment files=\"\u002Fhdri\u002Fwarehouse.hdr\" \u002F>\n\n\u002F\u002F Simple 3-point lighting\n\u003CambientLight intensity={0.3} \u002F>\n\u003CdirectionalLight position={[5, 5, 5]} intensity={1} castShadow \u002F>\n\u003CdirectionalLight position={[-3, 3, -5]} intensity={0.5} \u002F>\n\n\u002F\u002F Contact shadows (cheap fake shadows)\n\u003CContactShadows\n  position={[0, -1, 0]}\n  opacity={0.5}\n  scale={10}\n  blur={2}\n\u002F>\n```\n\n### Environment Presets (drei)\n\nAvailable: `apartment`, `city`, `dawn`, `forest`, `lobby`, `night`, `park`, `studio`, `sunset`, `warehouse`\n\n---\n\n## 6. Common Patterns\n\n### Product Reveal on Scroll\n\n```jsx\nfunction ProductReveal() {\n  return (\n    \u003CCanvas>\n      \u003CScrollControls pages={4} damping={0.3}>\n        \u003CScroll>\n          \u003CProductModel \u002F>  {\u002F* rotates with scroll *\u002F}\n        \u003C\u002FScroll>\n        \u003CScroll html>\n          \u003Csection style={{ top: \"100vh\" }}>Feature 1\u003C\u002Fsection>\n          \u003Csection style={{ top: \"200vh\" }}>Feature 2\u003C\u002Fsection>\n          \u003Csection style={{ top: \"300vh\" }}>CTA\u003C\u002Fsection>\n        \u003C\u002FScroll>\n      \u003C\u002FScrollControls>\n      \u003CEnvironment preset=\"studio\" \u002F>\n    \u003C\u002FCanvas>\n  );\n}\n```\n\n### Floating Objects (Parallax)\n\n```jsx\nimport { Float } from \"@react-three\u002Fdrei\";\n\n\u003CFloat\n  speed={2}           \u002F\u002F animation speed\n  rotationIntensity={0.5}\n  floatIntensity={0.5}\n>\n  \u003Cmesh>\n    \u003CtorusGeometry args={[1, 0.3, 16, 32]} \u002F>\n    \u003CmeshStandardMaterial color=\"hotpink\" \u002F>\n  \u003C\u002Fmesh>\n\u003C\u002FFloat>\n```\n\n### Text in 3D\n\n```jsx\nimport { Text3D, Center } from \"@react-three\u002Fdrei\";\n\n\u003CCenter>\n  \u003CText3D\n    font=\"\u002Ffonts\u002Finter_bold.json\"\n    size={0.75}\n    height={0.2}\n    curveSegments={12}\n  >\n    Hello World\n    \u003CmeshNormalMaterial \u002F>\n  \u003C\u002FText3D>\n\u003C\u002FCenter>\n```\n\n---\n\n## Workflow\n\n1. **Define the 3D need** — is it product showcase, decoration, interactive?\n2. **Pick approach** — R3F for code control, Spline for no-code, OGL for shader-only\n3. **Prepare models** — export GLTF\u002FGLB, optimize with gltf-transform\n4. **Set up scene** — Camera, lighting, environment\n5. **Add scroll interaction** — ScrollControls or GSAP integration\n6. **Optimize** — polygon budget, texture compression, instancing\n7. **Test performance** — Stats component, Chrome DevTools, test on mobile\n8. **Add fallbacks** — static image for low-end devices, loading state\n","# 3D Scene Skill\n\n## Decision Matrix — Pick Approach\n\n| Need | Approach | Bundle Impact | Complexity |\n|------|----------|---------------|------------|\n| Product showcase with scroll | R3F + ScrollControls | ~150KB (Three.js) | Medium |\n| Hero 3D scene (no-code) | Spline embed | ~0KB (iframe) | Low |\n| Custom shaders + 3D | Three.js vanilla | ~150KB | High |\n| Lightweight shader-only | OGL | ~8KB | Medium |\n| Interactive configurator | R3F + Leva\u002Fdat.gui | ~150KB | Medium |\n\n---\n\n## 1. React Three Fiber (R3F) + Drei [E1]\n\n**Install:**\n```bash\nnpm i three @react-three\u002Ffiber @react-three\u002Fdrei\n```\n\n### Basic Scene\n\n```jsx\nimport { Canvas } from \"@react-three\u002Ffiber\";\nimport { OrbitControls, Environment, useGLTF } from \"@react-three\u002Fdrei\";\n\nfunction Model({ url }) {\n  const { scene } = useGLTF(url);\n  return \u003Cprimitive object={scene} \u002F>;\n}\n\nfunction Scene() {\n  return (\n    \u003CCanvas\n      camera={{ position: [0, 2, 5], fov: 45 }}\n      style={{ width: \"100%\", height: \"100vh\" }}\n    >\n      \u003CEnvironment preset=\"studio\" \u002F>\n      \u003CModel url=\"\u002Fmodels\u002Fproduct.glb\" \u002F>\n      \u003COrbitControls enableZoom={false} \u002F>\n    \u003C\u002FCanvas>\n  );\n}\n```\n\n### ScrollControls (Scroll-Linked 3D) [E1]\n\n```jsx\nimport { Canvas, useFrame } from \"@react-three\u002Ffiber\";\nimport { ScrollControls, Scroll, useScroll } from \"@react-three\u002Fdrei\";\n\nfunction AnimatedModel() {\n  const scroll = useScroll();\n  const ref = useRef();\n\n  useFrame(() => {\n    const offset = scroll.offset; \u002F\u002F 0 to 1\n    \u002F\u002F Rotate model based on scroll\n    ref.current.rotation.y = offset * Math.PI * 2;\n    \u002F\u002F Move camera\u002Fmodel along a path\n    ref.current.position.y = offset * -5;\n  });\n\n  return (\n    \u003Cmesh ref={ref}>\n      \u003CboxGeometry args={[1, 1, 1]} \u002F>\n      \u003CmeshStandardMaterial color=\"royalblue\" \u002F>\n    \u003C\u002Fmesh>\n  );\n}\n\nfunction ScrollScene() {\n  return (\n    \u003CCanvas>\n      \u003CScrollControls\n        pages={5}       \u002F\u002F 5 pages of scroll (500vh)\n        damping={0.25}  \u002F\u002F friction (seconds to catch up)\n      >\n        {\u002F* 3D content *\u002F}\n        \u003CScroll>\n          \u003CAnimatedModel \u002F>\n        \u003C\u002FScroll>\n\n        {\u002F* HTML content overlaid *\u002F}\n        \u003CScroll html>\n          \u003Cdiv style={{ position: \"absolute\", top: \"100vh\" }}>\n            \u003Ch2>Section 2\u003C\u002Fh2>\n          \u003C\u002Fdiv>\n          \u003Cdiv style={{ position: \"absolute\", top: \"200vh\" }}>\n            \u003Ch2>Section 3\u003C\u002Fh2>\n          \u003C\u002Fdiv>\n        \u003C\u002FScroll>\n      \u003C\u002FScrollControls>\n\n      \u003CambientLight intensity={0.5} \u002F>\n      \u003CdirectionalLight position={[5, 5, 5]} \u002F>\n    \u003C\u002FCanvas>\n  );\n}\n```\n\n### useScroll Utilities\n\n```jsx\nconst scroll = useScroll();\n\n\u002F\u002F scroll.offset     — 0 to 1 (overall progress)\n\u002F\u002F scroll.delta      — scroll speed\n\u002F\u002F scroll.range(from, distance)  — 0-1 within range\n\u002F\u002F scroll.curve(from, distance)  — bell curve within range\n\u002F\u002F scroll.visible(from, distance) — boolean visibility\n\nuseFrame(() => {\n  \u002F\u002F Animate only in section 2 (20%-40% of scroll)\n  const sectionProgress = scroll.range(0.2, 0.2);\n  ref.current.scale.setScalar(1 + sectionProgress * 0.5);\n\n  \u002F\u002F Fade in section 3\n  const visible = scroll.visible(0.4, 0.2);\n  material.current.opacity = visible ? scroll.curve(0.4, 0.2) : 0;\n});\n```\n\n### R3F + GSAP (Alternative to ScrollControls) [E2]\n\nWhen you need GSAP's pin\u002Fsnap with 3D:\n\n```jsx\nimport { useGSAP } from \"@gsap\u002Freact\";\nimport gsap from \"gsap\";\nimport { ScrollTrigger } from \"gsap\u002FScrollTrigger\";\n\nfunction GSAPModel() {\n  const mesh = useRef();\n\n  useGSAP(() => {\n    gsap.to(mesh.current.rotation, {\n      y: Math.PI * 2,\n      scrollTrigger: {\n        trigger: \"#scene-container\",\n        start: \"top top\",\n        end: \"+=3000\",\n        pin: true,\n        scrub: 1,\n      }\n    });\n  });\n\n  return \u003Cmesh ref={mesh}>...\u003C\u002Fmesh>;\n}\n```\n\n---\n\n## 2. Spline Embeds [E2]\n\n**What:** No-code 3D design tool with web export.\n**Best for:** Quick 3D heroes, interactive product views, landing pages.\n\n### Embed Methods\n\n```jsx\n\u002F\u002F Method 1: iframe (simplest, no bundle impact)\n\u003Ciframe\n  src=\"https:\u002F\u002Fmy.spline.design\u002Fscene-abc123\u002F\"\n  frameBorder=\"0\"\n  width=\"100%\"\n  height=\"500px\"\n  style={{ border: \"none\" }}\n\u002F>\n\n\u002F\u002F Method 2: React component (more control)\nimport Spline from \"@splinetool\u002Freact-spline\";\n\n\u003CSpline scene=\"https:\u002F\u002Fprod.spline.design\u002Fabc123\u002Fscene.splinecode\" \u002F>\n\n\u002F\u002F Method 3: Vanilla JS\nimport { Application } from \"@splinetool\u002Fruntime\";\nconst app = new Application(canvas);\napp.load(\"https:\u002F\u002Fprod.spline.design\u002Fabc123\u002Fscene.splinecode\");\n```\n\n### Spline Capabilities\n\n- Scroll-linked animations (native events)\n- Mouse follow \u002F hover interactions\n- State changes on click\n- Physics simulations\n- Responsive layout\n- AI text-to-3D \u002F image-to-3D generation\n\n### Spline Limitations\n\n- **Performance:** Keep \u003C3 lights per scene\n- **Polygons:** Smooth subdivision max 2 levels\n- **Loading:** Scenes can be 1-5MB+ for complex objects\n- **Control:** Less fine-grained than custom Three.js\n- **Offline:** Requires network to load from Spline CDN (self-hosted export available)\n- **Scroll sync:** Less precise than R3F ScrollControls or GSAP\n\n### Spline Optimization\n\n- Delete invisible objects (inside or behind other objects)\n- Use \u003C3 lights\n- Compress on export (quality vs size tradeoff)\n- Reduce subdivision levels\n- Optimize CAD imports: strip internal geometry, small fillets\n- Keep total polygon count reasonable for target devices\n\n---\n\n## 3. GLTF\u002FGLB Loading & Optimization [E1]\n\n### The Pipeline\n\n```\nSource (FBX\u002FOBJ\u002FBlender)\n  → Export as GLTF\u002FGLB\n  → Optimize with gltf-transform\n    → Draco\u002FMeshopt compression\n    → KTX2 texture compression\n    → Mesh quantization\n  → Load in Three.js\u002FR3F\n```\n\n### gltf-transform Optimization [E1]\n\n```bash\n# Install\nnpm i -g @gltf-transform\u002Fcli\n\n# Full optimization pipeline\ngltf-transform optimize input.glb output.glb \\\n  --compress draco \\\n  --texture-compress webp\n\n# Or step by step:\ngltf-transform dedup input.glb deduped.glb           # remove duplicate data\ngltf-transform draco deduped.glb compressed.glb       # geometry compression\ngltf-transform webp compressed.glb textured.glb       # texture to WebP\ngltf-transform quantize textured.glb output.glb       # mesh quantization\n```\n\n### Real-World Size Reductions\n\n| Stage | Example Size | Reduction |\n|-------|-------------|-----------|\n| Raw FBX | 50 MB | baseline |\n| GLTF\u002FGLB export | 29 MB | -42% |\n| Draco compression | 5 MB | -83% |\n| + KTX2 textures | 2.5 MB | -91% |\n| + Mesh quantization | 2 MB | -93% |\n\n### Compression Methods\n\n| Method | Compression | Decode Speed | Three.js Version |\n|--------|-------------|--------------|-------------------|\n| Draco | Best (~90%) | Slower (Web Worker) | r100+ |\n| Meshopt | Good (~85%) | Faster | r122+ |\n| Quantization | Moderate (~50%) | Instant | r100+ |\n\n**Decision:** Draco for smallest files, Meshopt for fastest client decode. Combine with KTX2 textures.\n\n### Loading in R3F\n\n```jsx\nimport { useGLTF, useTexture } from \"@react-three\u002Fdrei\";\n\n\u002F\u002F Preload for instant display\nuseGLTF.preload(\"\u002Fmodels\u002Fproduct.glb\");\n\nfunction Product() {\n  const { scene, nodes, materials } = useGLTF(\"\u002Fmodels\u002Fproduct.glb\");\n\n  return (\n    \u003Cprimitive\n      object={scene}\n      scale={0.5}\n      position={[0, -1, 0]}\n    \u002F>\n  );\n}\n\n\u002F\u002F With Draco decoder\nimport { DRACOLoader } from \"three\u002Fexamples\u002Fjsm\u002Floaders\u002FDRACOLoader\";\n\n\u002F\u002F In Canvas setup or loader config:\n\u002F\u002F DRACOLoader points to decoder files (usually from CDN)\n```\n\n---\n\n## 4. Performance Budgets [E1]\n\n### Polygon Limits\n\n| Device Class | Max Triangles | Draw Calls | Notes |\n|-------------|---------------|------------|-------|\n| High-end desktop | 500K-1M | \u003C200 | Gaming GPU |\n| Average desktop | 100K-300K | \u003C100 | Integrated GPU |\n| Mobile (flagship) | 50K-150K | \u003C50 | iPhone 14+ level |\n| Mobile (budget) | 20K-50K | \u003C30 | Older Android |\n\n**Key insight:** Draw call count matters MORE than polygon count. Below 100 draw calls, most devices maintain 60fps. Above 500, even powerful GPUs struggle.\n\n### Texture Budgets\n\n| Texture | Max Size | Format | VRAM |\n|---------|----------|--------|------|\n| Diffuse\u002FAlbedo | 2048x2048 | KTX2\u002FWebP | ~5MB |\n| Normal map | 1024x1024 | KTX2 | ~1.3MB |\n| Roughness\u002FMetal | 512x512 | KTX2 | ~0.3MB |\n| Environment | 256x256 cube | HDR\u002FEXR | ~2MB |\n\n**Critical:** A 200KB PNG on disk = 20MB+ in VRAM! KTX2 with Basis Universal stays compressed on GPU (~10x reduction).\n\n### R3F Performance Tips\n\n```jsx\n\u002F\u002F 1. Use instancing for repeated objects\nimport { Instances, Instance } from \"@react-three\u002Fdrei\";\n\n\u003CInstances>\n  {positions.map((pos, i) => (\n    \u003CInstance key={i} position={pos} \u002F>\n  ))}\n\u003C\u002FInstances>\n\n\u002F\u002F 2. Frustum culling (enabled by default in Three.js)\n\u002F\u002F 3. LOD (Level of Detail)\nimport { Detailed } from \"@react-three\u002Fdrei\";\n\n\u003CDetailed distances={[0, 50, 100]}>\n  \u003CHighPolyModel \u002F>\n  \u003CMedPolyModel \u002F>\n  \u003CLowPolyModel \u002F>\n\u003C\u002FDetailed>\n\n\u002F\u002F 4. Limit pixel ratio on mobile\n\u003CCanvas dpr={[1, 2]}> {\u002F* max 2x, not device native *\u002F}\n\n\u002F\u002F 5. Use drei's Preload for loading screen\nimport { Preload } from \"@react-three\u002Fdrei\";\n\u003CCanvas>\n  \u003CSuspense fallback={null}>\n    \u003CScene \u002F>\n  \u003C\u002FSuspense>\n  \u003CPreload all \u002F>\n\u003C\u002FCanvas>\n```\n\n### Monitor Performance\n\n```jsx\nimport { useFrame } from \"@react-three\u002Ffiber\";\nimport { Stats } from \"@react-three\u002Fdrei\";\n\n\u002F\u002F Show FPS\u002FMS\u002FMB overlay\n\u003CStats \u002F>\n\n\u002F\u002F Or manual monitoring\nuseFrame((state) => {\n  const info = state.gl.info;\n  \u002F\u002F info.render.triangles — triangles per frame\n  \u002F\u002F info.render.calls — draw calls per frame\n  \u002F\u002F info.memory.textures — loaded textures\n  \u002F\u002F info.memory.geometries — loaded geometries\n});\n```\n\n---\n\n## 5. Lighting & Environment [E2]\n\n### Quick Setups (drei)\n\n```jsx\n\u002F\u002F Studio lighting (product showcase)\n\u003CEnvironment preset=\"studio\" \u002F>\n\n\u002F\u002F HDR environment\n\u003CEnvironment files=\"\u002Fhdri\u002Fwarehouse.hdr\" \u002F>\n\n\u002F\u002F Simple 3-point lighting\n\u003CambientLight intensity={0.3} \u002F>\n\u003CdirectionalLight position={[5, 5, 5]} intensity={1} castShadow \u002F>\n\u003CdirectionalLight position={[-3, 3, -5]} intensity={0.5} \u002F>\n\n\u002F\u002F Contact shadows (cheap fake shadows)\n\u003CContactShadows\n  position={[0, -1, 0]}\n  opacity={0.5}\n  scale={10}\n  blur={2}\n\u002F>\n```\n\n### Environment Presets (drei)\n\nAvailable: `apartment`, `city`, `dawn`, `forest`, `lobby`, `night`, `park`, `studio`, `sunset`, `warehouse`\n\n---\n\n## 6. Common Patterns\n\n### Product Reveal on Scroll\n\n```jsx\nfunction ProductReveal() {\n  return (\n    \u003CCanvas>\n      \u003CScrollControls pages={4} damping={0.3}>\n        \u003CScroll>\n          \u003CProductModel \u002F>  {\u002F* rotates with scroll *\u002F}\n        \u003C\u002FScroll>\n        \u003CScroll html>\n          \u003Csection style={{ top: \"100vh\" }}>Feature 1\u003C\u002Fsection>\n          \u003Csection style={{ top: \"200vh\" }}>Feature 2\u003C\u002Fsection>\n          \u003Csection style={{ top: \"300vh\" }}>CTA\u003C\u002Fsection>\n        \u003C\u002FScroll>\n      \u003C\u002FScrollControls>\n      \u003CEnvironment preset=\"studio\" \u002F>\n    \u003C\u002FCanvas>\n  );\n}\n```\n\n### Floating Objects (Parallax)\n\n```jsx\nimport { Float } from \"@react-three\u002Fdrei\";\n\n\u003CFloat\n  speed={2}           \u002F\u002F animation speed\n  rotationIntensity={0.5}\n  floatIntensity={0.5}\n>\n  \u003Cmesh>\n    \u003CtorusGeometry args={[1, 0.3, 16, 32]} \u002F>\n    \u003CmeshStandardMaterial color=\"hotpink\" \u002F>\n  \u003C\u002Fmesh>\n\u003C\u002FFloat>\n```\n\n### Text in 3D\n\n```jsx\nimport { Text3D, Center } from \"@react-three\u002Fdrei\";\n\n\u003CCenter>\n  \u003CText3D\n    font=\"\u002Ffonts\u002Finter_bold.json\"\n    size={0.75}\n    height={0.2}\n    curveSegments={12}\n  >\n    Hello World\n    \u003CmeshNormalMaterial \u002F>\n  \u003C\u002FText3D>\n\u003C\u002FCenter>\n```\n\n---\n\n## Workflow\n\n1. **Define the 3D need** — is it product showcase, decoration, interactive?\n2. **Pick approach** — R3F for code control, Spline for no-code, OGL for shader-only\n3. **Prepare models** — export GLTF\u002FGLB, optimize with gltf-transform\n4. **Set up scene** — Camera, lighting, environment\n5. **Add scroll interaction** — ScrollControls or GSAP integration\n6. **Optimize** — polygon budget, texture compression, instancing\n7. **Test performance** — Stats component, Chrome DevTools, test on mobile\n8. **Add fallbacks** — static image for low-end devices, loading state\n",{"name":5,"arguments":49,"description":7},[50,53],{"name":51,"required":45,"description":52},"approach","Approach: r3f, threejs-vanilla, spline-embed (auto-detect if omitted)",{"name":31,"required":45,"description":54},"Interaction: scroll-linked, orbit, hover, static (default: scroll-linked)","main","32e85218a997294f68da27ff3dfd638196186ae5","skills\u002F3d-scene",8,"https:\u002F\u002Fgithub.com\u002FKeiSeiLab\u002FKeiSeiKit","https:\u002F\u002Fgithub.com\u002FKeiSeiLab","2026-10-11 11:17:46",{"source":8,"skillName":5,"commands":63,"prompt":73,"zipAvailable":74},[64,67,70],{"tool":65,"command":66},"npx","npx skills add KeiSeiLab\u002FKeiSeiKit --skill 3d-scene",{"tool":68,"command":69},"bunx","bunx skills add KeiSeiLab\u002FKeiSeiKit --skill 3d-scene",{"tool":71,"command":72},"pnpm","pnpm dlx skills add KeiSeiLab\u002FKeiSeiKit --skill 3d-scene","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FKeiSeiLab\u002FKeiSeiKit\u002Fblob\u002Fmain\u002Fskills\u002F3d-scene\u002FSKILL.md",true,[],[77,107,131,164,190,216],{"skillNo":78,"name":79,"nameZh":6,"description":80,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":81,"categories":82,"keywords":86,"publishedAt":44},"01M71K887N78V089XR5BWT0JAV","wave-audit","3-wave parallel audit — Wave 1 discovery (4 agents), Wave 2 verification (4 agents), Wave 3 cross-optimization (1 synthesizer). Each wave runs agents in parallel. Combines security review, runtime analysis, quality check, and constructor pattern into one unified workflow with evidence grading and Go\u002FNo-Go verdict.","https:\u002F\u002Fgithub.com\u002FKeiSeiLab\u002FKeiSeiKit\u002Fblob\u002Fmain\u002Fskills\u002Fwave-audit\u002FSKILL.md",[83],{"categoryNo":84,"categoryName":85,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106],"wave audit","audit","unified workflow","workflow","analysis","review","verification","security review","pattern","grading","go","security","parallel","evidence","runtime","quality check","discovery","Optimization","one","check",{"skillNo":108,"name":109,"nameZh":6,"description":110,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":111,"categories":112,"keywords":113,"publishedAt":44},"01M71K85100JQS4Y10EGCTWQXW","vm-provision","End-to-end VPS provisioning — select provider → plan → provision → harden → verify (ssh-check + firewall-diff hard-gate) → handoff. 6 phases, ≥6 AskUserQuestion calls, defensive-only. Stops if either verification primitive fails.","https:\u002F\u002Fgithub.com\u002FKeiSeiLab\u002FKeiSeiKit\u002Fblob\u002Fmain\u002Fskills\u002Fvm-provision\u002FSKILL.md",[],[114,115,116,117,118,119,120,121,122,123,105,124,125,106,93,126,127,128,129,130],"vm","vm provision","firewall","diff","provider","select","end","calls","only","either","api","hetzner","plan","defensive","verify","intent","line",{"skillNo":132,"name":133,"nameZh":6,"description":134,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":135,"licenseFileUrl":6,"sourceHtmlUrl":136,"categories":137,"keywords":144,"publishedAt":44},"01M71K80ZRH2H5ES83QS97BB91","visual-explainer","Generate beautiful, self-contained HTML pages that visually explain systems, code changes, plans, and data. Use when the user asks for a diagram, architecture overview, diff review, plan review, project recap, comparison table, or any visual explanation of technical concepts. Also use proactively when you are about to render a complex ASCII table (4+ rows or 3+ columns) — present it as a styled HTML page instead.","MIT","https:\u002F\u002Fgithub.com\u002FKeiSeiLab\u002FKeiSeiKit\u002Fblob\u002Fmain\u002Fskills\u002Fvisual-explainer\u002FSKILL.md",[138,139],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":140,"categoryName":141,"taxonomyType":142,"taxonomyTypeDesc":143},"01JBSEEDCAT000000000000033","数据分析师","PROFESSION","适用职业",[145,146,147,148,149,150,151,152,126,153,154,155,156,157,158,159,160,161,162,163],"visual","visual explainer","explainer","project","files","image","you","architecture","generate","present","concepts","table","as","view","for","ascii table","ascii","surf","use",{"skillNo":165,"name":166,"nameZh":6,"description":167,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":168,"categories":169,"keywords":173,"publishedAt":44},"01M71K7VSD5ZWN26QJEC4T0770","video-gen","Use when generating video from frame sequences — FFmpeg extraction, WebP\u002FAVIF conversion, sprite sheets, scroll-synced playback, video encoding\u002Ftranscoding. Covers the full pipeline from video source to web-ready frame sequence or optimized video.","https:\u002F\u002Fgithub.com\u002FKeiSeiLab\u002FKeiSeiKit\u002Fblob\u002Fmain\u002Fskills\u002Fvideo-gen\u002FSKILL.md",[170],{"categoryNo":171,"categoryName":172,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000016","图像与多媒体",[174,175,176,177,178,37,179,180,34,33,150,181,182,183,184,185,186,187,188,189],"Video","gen","video gen","ready","pipeline","generated","synced","conversion","directory","sequences","sequence","source","full","path","webp","target",{"skillNo":191,"name":192,"nameZh":6,"description":193,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":194,"categories":195,"keywords":199,"publishedAt":44},"01M71K7R88RM8ENVCRZHZSMM5T","test-matrix","Use when a project needs testing BEYOND unit tests — fuzzing, property-based, load, E2E, or mutation. Five-phase hub-and-spoke pipeline composes the right mix per language × critical path × CI target, scaffolds configs + corpus + fixtures, wires CI jobs, and defines the crash\u002Fregression triage workflow. Pure-click: every decision except intake is an AskUserQuestion.","https:\u002F\u002Fgithub.com\u002FKeiSeiLab\u002FKeiSeiKit\u002Fblob\u002Fmain\u002Fskills\u002Ftest-matrix\u002FSKILL.md",[196],{"categoryNo":197,"categoryName":198,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量",[200,201,202,203,189,204,148,205,178,206,207,208,209,210,211,212,213,163,214,215],"test","matrix","test matrix","unit","click","regression","mix","intake","testing","decision","ci","tests","e2e","mutation","load","fuzzing",{"skillNo":217,"name":218,"nameZh":6,"description":219,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":220,"categories":221,"keywords":223,"publishedAt":44},"01M71K7N3ZY9FMMH3CSE1H8K5J","test-gen","Use when generating tests for untested code — happy path, edge cases, error handling","https:\u002F\u002Fgithub.com\u002FKeiSeiLab\u002FKeiSeiKit\u002Fblob\u002Fmain\u002Fskills\u002Ftest-gen\u002FSKILL.md",[222],{"categoryNo":197,"categoryName":198,"taxonomyType":21,"taxonomyTypeDesc":22},[224,175,200,225,189,159,226,211,227,163,33,228,229,230,231,187],"test gen","error","true","edge","file","code","error handling","name"]