` - Graphics store (required)
- `id: string` - Unique identifier (required)
- `geometry: GeometryConfig` - Geometry configuration (required)
- `material: MaterialConfig` - Material configuration (required)
- `position: [number, number, number]` - Position (required)
- `rotation: [number, number, number]` - Rotation in radians (default: [0, 0, 0])
- `scale: [number, number, number]` - Scale (default: [1, 1, 1])
- `visible: boolean` - Visibility (default: true)
**Lifecycle**:
- `onMount`: Dispatches `addMesh` action
- Props change: Dispatches `updateMesh` action
- `onDestroy`: Dispatches `removeMesh` action
**Usage**:
```typescript
```
---
## GEOMETRY TYPES
### Box
Rectangular prism.
**Config**:
```typescript
{ type: 'box'; size: number }
```
**Example**:
```typescript
```
### Sphere
Spherical geometry.
**Config**:
```typescript
{
type: 'sphere';
radius: number;
segments?: number; // Default: 32 (higher = smoother)
}
```
**Example**:
```typescript
```
**Segments**: Higher values create smoother spheres but increase draw calls.
- Low poly (16 segments): Retro/stylized look
- Medium (32 segments): Default, good balance
- High poly (64 segments): Smooth, more expensive
### Cylinder
Cylindrical geometry.
**Config**:
```typescript
{
type: 'cylinder';
height: number;
diameter: number;
}
```
**Example**:
```typescript
```
### Torus
Donut-shaped geometry.
**Config**:
```typescript
{
type: 'torus';
diameter: number; // Outer diameter
thickness: number; // Tube thickness
segments?: number; // Default: 32
}
```
**Example**:
```typescript
```
### Plane
Flat rectangular surface.
**Config**:
```typescript
{
type: 'plane';
width: number;
height: number;
}
```
**Example**:
```typescript
```
**Note**: Planes are initially vertical (facing Z-axis). Rotate by `[Math.PI / 2, 0, 0]` to make horizontal (ground).
---
## MATERIAL PROPERTIES
**MaterialConfig Interface**:
```typescript
interface MaterialConfig {
color: string; // Hex or CSS color
metallic?: number; // 0-1 (default: 0.5)
roughness?: number; // 0-1 (default: 0.5)
emissive?: string; // Emissive color (optional)
alpha?: number; // 0-1 transparency (optional)
wireframe?: boolean; // Wireframe mode (default: false)
}
```
**PBR Workflow**:
Materials use Physically Based Rendering (PBR) with metallic/roughness workflow.
### Metallic (0-1)
Controls how metal-like the surface appears.
- `0.0`: Non-metallic (plastic, rubber, wood, stone)
- `0.5`: Semi-metallic (painted metal, worn surfaces)
- `1.0`: Fully metallic (polished metal, chrome)
**Examples**:
```typescript
// Plastic
{ color: '#ff0000', metallic: 0.0, roughness: 0.5 }
// Painted metal
{ color: '#4ecdc4', metallic: 0.5, roughness: 0.4 }
// Polished chrome
{ color: '#ffffff', metallic: 1.0, roughness: 0.1 }
```
### Roughness (0-1)
Controls surface smoothness/reflectivity.
- `0.0`: Mirror-smooth (glossy, high reflections)
- `0.5`: Semi-rough (satin finish)
- `1.0`: Very rough (matte, diffuse)
**Examples**:
```typescript
// Glass/mirror
{ color: '#ffffff', metallic: 0.0, roughness: 0.0 }
// Satin finish
{ color: '#ff6b6b', metallic: 0.3, roughness: 0.5 }
// Matte rubber
{ color: '#333333', metallic: 0.0, roughness: 1.0 }
```
### Common Material Presets
```typescript
// Polished gold
const gold = { color: '#ffd700', metallic: 1.0, roughness: 0.2 };
// Brushed aluminum
const aluminum = { color: '#c0c0c0', metallic: 1.0, roughness: 0.4 };
// Copper
const copper = { color: '#b87333', metallic: 1.0, roughness: 0.3 };
// Wood
const wood = { color: '#8b4513', metallic: 0.0, roughness: 0.8 };
// Plastic
const plastic = { color: '#ff6b6b', metallic: 0.0, roughness: 0.4 };
// Stone
const stone = { color: '#808080', metallic: 0.0, roughness: 0.9 };
// Rubber
const rubber = { color: '#1a1a1a', metallic: 0.0, roughness: 1.0 };
```
---
## COMPLETE EXAMPLE
Full scene with all geometry types:
```typescript
{#if $store.renderer.isInitialized}
Renderer: {$store.renderer.activeRenderer?.toUpperCase()}
Max Texture: {$store.renderer.capabilities.maxTextureSize}px
{:else if $store.renderer.error}
Error: {$store.renderer.error}
{:else}
Initializing...
{/if}
```
---
## STATE MANAGEMENT
### GraphicsState Interface
```typescript
interface GraphicsState {
// Renderer
renderer: {
activeRenderer: 'webgpu' | 'webgl' | null;
isInitialized: boolean;
capabilities: {
supportsWebGPU: boolean;
supportsWebGL: boolean;
maxTextureSize: number;
maxVertexAttributes: number;
};
error: string | null;
};
// Scene
scene: SceneNode;
backgroundColor: string;
// Camera
camera: CameraConfig;
// Lights
lights: LightConfig[];
// Meshes
meshes: MeshConfig[];
// Animations (future)
animations: AnimationState[];
// Loading
isLoading: boolean;
loadingProgress: number; // 0-1
}
```
### GraphicsAction Types
```typescript
type GraphicsAction =
// Renderer
| { type: 'rendererInitialized'; renderer: 'webgpu' | 'webgl'; capabilities: RendererCapabilities }
| { type: 'rendererError'; error: string }
// Camera
| { type: 'updateCamera'; camera: Partial }
| { type: 'setCameraPosition'; position: Vector3 }
| { type: 'setCameraLookAt'; lookAt: Vector3 }
// Mesh
| { type: 'addMesh'; mesh: MeshConfig }
| { type: 'removeMesh'; id: string }
| { type: 'updateMesh'; id: string; updates: Partial }
| { type: 'setMeshPosition'; id: string; position: Vector3 }
| { type: 'setMeshRotation'; id: string; rotation: Vector3 }
| { type: 'setMeshScale'; id: string; scale: Vector3 }
| { type: 'toggleMeshVisibility'; id: string }
// Light
| { type: 'addLight'; light: LightConfig }
| { type: 'removeLight'; index: number }
| { type: 'updateLight'; index: number; light: Partial }
// Scene
| { type: 'setBackgroundColor'; color: string }
| { type: 'clearScene' };
```
### Reducer Pattern
Graphics reducer is pure and testable:
```typescript
import { graphicsReducer } from '@composable-svelte/graphics';
import { TestStore } from '@composable-svelte/core';
const store = new TestStore({
initialState: createInitialGraphicsState(),
reducer: graphicsReducer,
dependencies: {}
});
// Add mesh
await store.send({
type: 'addMesh',
mesh: {
id: 'test-cube',
geometry: { type: 'box', size: 1 },
material: { color: '#ff0000' },
position: [0, 0, 0]
}
}, (state) => {
expect(state.meshes.length).toBe(1);
expect(state.meshes[0].id).toBe('test-cube');
});
// Update position
await store.send({
type: 'setMeshPosition',
id: 'test-cube',
position: [1, 2, 3]
}, (state) => {
expect(state.meshes[0].position).toEqual([1, 2, 3]);
});
```
---
## PERFORMANCE CONSIDERATIONS
### Geometry Complexity
**Segments**: Higher segment counts create smoother geometry but increase draw calls.
```typescript
// Low poly (fast, retro look)
geometry={{ type: 'sphere', radius: 1, segments: 16 }}
// Default (good balance)
geometry={{ type: 'sphere', radius: 1, segments: 32 }}
// High poly (slow, smooth)
geometry={{ type: 'sphere', radius: 1, segments: 64 }}
```
### Draw Calls
Each mesh = 1 draw call. Minimize meshes for better performance.
**Good**:
```typescript
// 3 meshes = 3 draw calls
```
**Bad**:
```typescript
// 1000 meshes = 1000 draw calls (very slow!)
{#each items as item}
{/each}
```
**Solution**: Use instancing for many similar objects (future feature).
### Update Frequency
Scene sync uses deep comparison (JSON.stringify). Avoid updating mesh props every frame.
**Good**:
```typescript
// Update rotation only when button clicked
let rotation = $state(0);
function rotate() { rotation += Math.PI / 4; }
```
**Bad**:
```typescript
// Updates every frame (60 FPS) - expensive!
let time = $state(0);
setInterval(() => { time += 0.01; }, 16);
```
**Solution**: Use animation system (future feature) or throttle updates.
---
## COMMON PATTERNS
### Rotation Animation
```typescript
let rotation = $state(0);
function rotateObject() {
rotation += Math.PI / 4; // 45 degrees
}
```
### Camera Controls
```typescript
let cameraDistance = $state(12);
function zoomIn() {
cameraDistance = Math.max(5, cameraDistance - 2);
}
function zoomOut() {
cameraDistance = Math.min(20, cameraDistance + 2);
}
```
### Dynamic Lighting
```typescript
let lightIntensity = $state(1.0);
function adjustBrightness(delta: number) {
lightIntensity = Math.max(0, Math.min(2, lightIntensity + delta));
}
```
### Toggle Visibility
```typescript
let showObject = $state(true);
// Option 1: Conditional rendering
{#if showObject}
{/if}
// Option 2: Visible prop
```
---
## FUTURE FEATURES
These features are planned but not yet implemented:
### Custom Shaders
```typescript
// Future API
```
### Textures
```typescript
// Future API
```
### Animations
```typescript
// Future API
```
### Post-Processing
```typescript
// Future API
...
```
---
## CROSS-REFERENCES
**Related Skills**:
- **composable-svelte-core**: Store, reducer, Effect system
- **composable-svelte-components**: UI components that complement 3D scenes
- **composable-svelte-testing**: TestStore for testing graphics reducers
**When to Use Each Package**:
- **graphics**: 3D scenes, WebGPU/WebGL rendering
- **charts**: 2D data visualization (see composable-svelte-charts)
- **maps**: Geospatial data (see composable-svelte-maps)
- **code**: Code editors, syntax highlighting (see composable-svelte-code)
---
## TROUBLESHOOTING
**Scene not rendering**:
- Check browser WebGPU/WebGL support
- Verify store is created with `graphicsReducer`
- Check console for renderer errors in `$store.renderer.error`
**Objects not visible**:
- Ensure Camera is pointing at objects (`lookAt` prop)
- Add at least one Light (scene is dark by default)
- Check mesh `visible` prop
- Verify position values (objects might be off-screen)
**Poor performance**:
- Reduce segment counts on spheres/toruses
- Minimize number of meshes (each mesh = 1 draw call)
- Avoid updating mesh props every frame
- Use simpler geometry (box vs sphere)
**TypeScript errors**:
- Ensure `@composable-svelte/graphics` is installed
- Check geometry config matches type (e.g., `box` requires `size`, not `radius`)
- Verify Vector3 arrays are exactly 3 numbers `[x, y, z]`