> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/BintzGavin/helios/llms.txt
> Use this file to discover all available pages before exploring further.

# Three.js 3D rendering

> Render 3D scenes with Three.js and Helios for video export

Three.js is a powerful 3D graphics library built on WebGL. Helios provides precise frame control for rendering Three.js scenes to video.

## Basic setup

Three.js requires manual rendering driven by Helios instead of using `requestAnimationFrame`.

```typescript theme={null}
import * as THREE from 'three';
import { Helios } from '@helios-project/core';

// Setup Three.js
const canvas = document.getElementById('composition-canvas') as HTMLCanvasElement;
if (!canvas) throw new Error('Canvas not found');

const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
const scene = new THREE.Scene();

// Camera
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
camera.position.z = 5;

// Lighting
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(1, 1, 1).normalize();
scene.add(light);

const ambientLight = new THREE.AmbientLight(0x404040);
scene.add(ambientLight);

// Object
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshPhongMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

// Resize Logic
const onResize = () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
};

window.addEventListener('resize', onResize);
onResize();

// Setup Helios
const helios = new Helios({ duration: 5, fps: 30 });
helios.bindToDocumentTimeline();

// Drive Animation
function draw(frame: number) {
  const time = frame / 30; // fps hardcoded to match Helios config

  // Rotate cube based on time
  cube.rotation.x = time;
  cube.rotation.y = time;

  renderer.render(scene, camera);
}

helios.subscribe((state) => {
  draw(state.currentFrame);
});

// Initial draw
draw(0);

// Expose for debugging
(window as any).helios = helios;
(window as any).scene = scene;
```

## React Three Fiber integration

For React applications, use `@react-three/fiber` with Helios controlling the frame loop.

```typescript theme={null}
import React, { useState, useEffect } from 'react';
import { Canvas, RootState } from '@react-three/fiber';
import { Helios } from '@helios-project/core';
import Scene from './Scene';

// Singleton Helios instance
const helios = new Helios({
  fps: 30,
  duration: 10,
  autoSyncAnimations: true
});

helios.bindToDocumentTimeline();

if (typeof window !== 'undefined') {
  (window as any).helios = helios;
}

export default function App() {
  const [r3fState, setR3fState] = useState<RootState | null>(null);

  useEffect(() => {
    if (!r3fState) return;

    // Drive R3F loop manually
    return helios.subscribe((state) => {
      // R3F's advance(timestamp) sets the internal clock.elapsedTime
      // We pass time in seconds because Three.js clocks work in seconds
      const timeInSeconds = state.currentFrame / state.fps;

      // Explicitly advance the R3F state
      r3fState.advance(timeInSeconds);
    });
  }, [r3fState]);

  return (
    <Canvas
      frameloop="never"
      onCreated={(state) => setR3fState(state)}
      style={{ width: '100%', height: '100%', background: '#111' }}
      camera={{ position: [0, 0, 5] }}
    >
      <ambientLight intensity={0.5} />
      <pointLight position={[10, 10, 10]} />
      <Scene />
    </Canvas>
  );
}
```

### Scene component

```typescript theme={null}
import React, { useRef } from 'react';
import { useFrame } from '@react-three/fiber';
import { Mesh } from 'three';

export default function Scene() {
  const meshRef = useRef<Mesh>(null);

  useFrame((state) => {
    // state.clock.elapsedTime matches what we passed to advance()
    const t = state.clock.elapsedTime;
    if (meshRef.current) {
      meshRef.current.rotation.x = t;
      meshRef.current.rotation.y = t * 0.5;
    }
  });

  return (
    <mesh ref={meshRef}>
      <boxGeometry args={[2, 2, 2]} />
      <meshStandardMaterial color="orange" />
    </mesh>
  );
}
```

## Critical patterns

### Disable automatic rendering

Three.js and React Three Fiber both have built-in render loops. You must disable them:

* **Vanilla Three.js**: Don't call `requestAnimationFrame` or `renderer.setAnimationLoop()`
* **React Three Fiber**: Set `frameloop="never"` on the `<Canvas>` component

### Use Helios time, not system time

Always calculate animations from `state.currentFrame / fps` instead of `Date.now()` or `performance.now()`:

```typescript theme={null}
helios.subscribe((state) => {
  const time = state.currentFrame / state.fps; // Deterministic
  cube.rotation.x = time; // Repeatable across renders
});
```

### Handle resize properly

Update camera aspect ratio and renderer size on window resize:

```typescript theme={null}
const onResize = () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
};

window.addEventListener('resize', onResize);
```

## Performance considerations

### WebGL context limits

Browsers limit the number of WebGL contexts. Destroy renderers when unmounting:

```typescript theme={null}
useEffect(() => {
  const renderer = new THREE.WebGLRenderer();
  
  return () => {
    renderer.dispose();
    // Also dispose geometries and materials
    geometry.dispose();
    material.dispose();
  };
}, []);
```

### Scene complexity

Three.js performance depends on:

* **Polygon count**: Keep meshes under 100k triangles for smooth rendering
* **Draw calls**: Merge geometries to reduce draw calls
* **Texture size**: Use power-of-2 textures (512x512, 1024x1024) for GPU efficiency
* **Lighting**: Limit real-time shadows and use baked lighting where possible

### Rendering resolution

Match the renderer size to your target export resolution:

```typescript theme={null}
// For 1080p export
renderer.setSize(1920, 1080);

// For 4K export
renderer.setSize(3840, 2160);
```

Higher resolutions increase render time proportionally.

### Frame rate vs quality

Lower FPS reduces total render time but may affect motion smoothness:

* **30 fps**: Standard for most content, good balance
* **60 fps**: Smooth motion, doubles render time
* **24 fps**: Cinematic feel, fastest render

## Package dependencies

```json theme={null}
{
  "dependencies": {
    "three": "^0.182.0",
    "@helios-project/core": "latest"
  },
  "devDependencies": {
    "typescript": "^5.0.0",
    "vite": "^7.1.2"
  }
}
```

For React Three Fiber:

```json theme={null}
{
  "dependencies": {
    "three": "^0.182.0",
    "@react-three/fiber": "^8.0.0",
    "@helios-project/core": "latest",
    "react": "^18.0.0",
    "react-dom": "^18.0.0"
  }
}
```

## Common issues

### Black screen on render

Ensure you have lighting in your scene:

```typescript theme={null}
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(1, 1, 1);
scene.add(light);

const ambient = new THREE.AmbientLight(0x404040);
scene.add(ambient);
```

### Animation not playing

Verify Helios is bound to the document timeline:

```typescript theme={null}
helios.bindToDocumentTimeline();
```

And that you're subscribing to state changes:

```typescript theme={null}
helios.subscribe((state) => {
  // Update scene based on state.currentFrame
});
```

### Canvas not resizing

Update both camera and renderer on resize events:

```typescript theme={null}
window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
});
```
