Three.js game development with React using @react-three/fiber and @react-three/drei with strict TypeScript and 60fps performance
Applies when building 3D scenes, implementing game loops, handling 3D interactions, optimizing Three.js rendering, loading assets, or managing game state with React.
@react-three/fiber JSX, not imperative Three.jsuseRef<THREE.Mesh>(null), typed props, typed event handlersuseFrame for the game loop — ((state, delta) => …) — delta time, not wall-clockuseStateOrbitControls, useTexture, Html, Sparkles, TrailonClick, onPointerOver on meshes — no manual raycastinguseState inside useFrame — use refs for transient animation stateprefers-reduced-motion, readable HUD contrastuseFrameimport { useRef } from 'react';
import { useFrame } from '@react-three/fiber';
import * as THREE from 'three';
interface TargetProps {
position: readonly [number, number, number];
size: number;
onClick: () => void;
}
export function Target({ position, size, onClick }: TargetProps): JSX.Element {
const meshRef = useRef<THREE.Mesh>(null);
useFrame((state, delta) => {
const mesh = meshRef.current;
if (!mesh) return;
mesh.rotation.y += delta * 0.5;
mesh.position.y = position[1] + Math.sin(state.clock.elapsedTime) * 0.3;
});
return (
<mesh ref={meshRef} position={position} onClick={onClick}>
<sphereGeometry args={[size, 16, 16]} />
<meshStandardMaterial color="hotpink" />
</mesh>
);
}
import { useEffect, useMemo } from 'react';
import * as THREE from 'three';
function Ring(): JSX.Element {
const geometry = useMemo(() => new THREE.TorusGeometry(1, 0.2, 16, 64), []);
const material = useMemo(() => new THREE.MeshStandardMaterial({ color: 'cyan' }), []);
useEffect(() => {
return () => {
geometry.dispose();
material.dispose();
};
}, [geometry, material]);
return <mesh geometry={geometry} material={material} />;
}
const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
useFrame((_, delta) => {
const speed = prefersReduced ? 0 : 0.5;
if (meshRef.current) meshRef.current.rotation.y += delta * speed;
});
// BAD: setInterval for animation
setInterval(() => { mesh.rotation.y += 0.01 }, 16);
// BAD: useState in useFrame (60 renders/sec)
useFrame(() => setPosition(p => p + 1));
// BAD: untyped ref
const meshRef = useRef(null);
// BAD: forgetting disposal
// Leaks geometry and material on unmount
// BAD: loading textures from user-supplied URLs without validation
useTexture(userInputUrl);
useRef<THREE.X>(null))useFrame + delta; no timersuseState inside useFrameInstancedMeshprefers-reduced-motion honoredSearch for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer