3D Model Viewer Test
✨ Now Using New Compound Component API!
This page now uses the refactored ModelViewer with Redux-like state management and compound components. Much more flexible with both props-based API (backward compatible) and compound component patterns.
This page demonstrates the 3D model viewer component built with React Three Fiber. You can orbit around the model by clicking and dragging, zoom with the scroll wheel, and pan by right-clicking and dragging.
Example 1: Props-Based API (Backward Compatible)
Simple props-based usage with auto-rotate and toon shading. Perfect for quick embedding without custom controls.
- Auto-Rotate - Model rotates automatically
- Toon Shading - Cel-shaded anime look
- Default Controls - Standard control panel with common features
<ModelViewer modelPath="..." autoRotate toonShading showControls />
Example 2: Compound Components (Custom Layout)
Full control over layout and which controls to show using compound components. Build your own custom control panel with only the features you need.
// In a React component file (.tsx):
<ModelViewer modelPath="...">
<ViewerCanvas height="500px" />
<ViewerControls>
<ViewControl.AutoRotate />
<ViewControl.ToonShading />
<CameraControl.CenterModel />
<AnimationControl.SimplePlayer />
</ViewerControls>
</ModelViewer> 💡 Tip: Create React wrapper components for compound layouts
Compound components must be used in .tsx files, not .astro files.
See src/components/react/3d/examples/CompoundExample.tsx for examples.
Example 3: Minimal Embedded Viewer
Compact viewer with no controls - just the model with auto-rotation. Perfect for inline embedding in blog posts or character showcases.
<ModelViewer modelPath="..." width=400 height=400 autoRotate />
Note: If a model appears without textures, check that textures are properly embedded
in the GLB file during export from Blender. See public/models/README.md for troubleshooting.
Example 4: Full Animation Controls
Advanced animation control with full playback UI including play/pause/stop, speed control, and loop modes. Perfect for character showcases with multiple animations.
// In a React component file (.tsx):
<ModelViewer modelPath="...">
<ViewerCanvas height="600px" />
<ViewerControls>
<ViewControl.ToonShading />
<CameraControl.PresetList />
<AnimationControl.Player />
</ViewerControls>
</ModelViewer> 🎬 Animation Features:
- Play/Pause/Stop - Full playback control
- Speed Control - Adjust animation speed (0.1x to 2.0x)
- Loop Modes - Once, Repeat, or Ping-Pong
- Animation List - Click buttons to switch animations
- Camera Presets - Switch between predefined camera angles
Example 5: Debug Mode with Helpers
Debug mode provides visualization helpers for development - bounding box, grid/axes,
and light helpers. Use controlTier="debug" to show all debug controls.
<ModelViewer modelPath="..." controlTier="debug" showControls />
🔧 Debug Features:
- Bounding Box - Shows model bounds for sizing
- Grid & Axes - XYZ axis helpers and ground grid
- Light Helpers - Visualize light positions and directions
- Keyboard Controls - WASD camera movement
How to Use (New Compound Component API)
1. Add Your Models
Place your 3D model files in the public/models/ directory.
Supported formats: FBX, GLTF, GLB
2. Import ModelViewer and Components
---
import ModelViewer, {
ViewerCanvas,
ViewerControls,
ViewControl,
CameraControl,
AnimationControl
} from '@components/react/3d';
--- 3. Props-Based API (Simple)
<ModelViewer
client:load
modelPath="/models/your-model.glb"
autoRotate={true}
toonShading={true}
showControls={true}
/> 4. Compound Component API (Flexible)
Important: Compound components must be used inside React (.tsx) files, not in Astro (.astro) files.
Create wrapper components in src/components/react/.
// CustomViewer.tsx (React component)
import ModelViewer, { ViewerCanvas, ViewerControls, ViewControl } from '@components/react/3d';
export const CustomViewer = ({ modelPath }) => (
<ModelViewer modelPath={modelPath}>
<ViewerCanvas height="600px" />
<ViewerControls title="My Controls">
<ViewControl.AutoRotate />
<ViewControl.ToonShading />
</ViewerControls>
</ModelViewer>
);
// Then in your .astro file:
---
import { CustomViewer } from '@components/react/CustomViewer';
---
<CustomViewer client:load modelPath="/models/model.glb" /> ⚠️ Why this restriction?
Astro evaluates JSX during the build/SSR phase before React context exists. Compound components need access to ViewerContext, which only exists at runtime in React. Wrapping them in a React component file (.tsx) ensures they're only evaluated in the React runtime.
5. Available Compound Components
ViewerCanvas- The 3D canvas (required)ViewerControls- Control panel wrapperViewControl.*- AutoRotate, ToonShading, Wireframe, Specular, ResetViewportCameraControl.*- PresetSelector, PresetList, CenterModel, KeyboardToggleAnimationControl.*- Player, SimplePlayer, AnimationListDebugControl.*- BoundingBox, GridHelpers, LightHelpers, DebugMode
6. Essential Props
modelPath- Path to your 3D model file (required)width- Canvas width (default: "100%")height- Canvas height (default: "600px")autoRotate- Enable auto-rotation (default: false)toonShading- Enable cel-shading (default: false)showControls- Show default control panel (default: true)hideControls- Array of control IDs to hide
7. Controls
- Left Click + Drag: Orbit/rotate around model
- Right Click + Drag: Pan camera
- Scroll Wheel: Zoom in/out
- WASD Keys: Camera movement (when enabled)
- Control Panel: Toggle features and animations
Benefits of the New API
- ✅ Flexible - Mix and match controls to build custom UIs
- ✅ Backward Compatible - Props-based API still works
- ✅ Redux-like State - Predictable state management with useReducer
- ✅ Compound Components - Compose your own control panels
- ✅ Promise-based Animations - Async/await for animation control
- ✅ Theme-aware - Automatically adapts to light/dark mode
- ✅ TypeScript - Full type safety with discriminated unions
Next Steps
- Add your FBX, GLTF, or GLB models to
public/models/ - Choose props-based API for quick embedding or compound components for custom layouts
- Use compound components to build exactly the control panel you need
- Embed the viewer in your comic pages or blog posts
- See
/3d-animation-testfor animation examples
� Troubleshooting Models Without Textures
If your model appears white or gray without textures:
- Check texture embedding - GLB files must have textures embedded during export
- Verify Blender export settings:
- Format: GLB Binary (not separate .gltf)
- ✓ Include: Textures checked
- ✓ Materials: Export checked
- Use Principled BSDF with connected Image Texture nodes
- File size check - A character with textures should be 2-10MB (not <100KB)
- Try debug mode - Set
controlTier="debug"to inspect materials - Enable toon shading - Sometimes helps with solid-color models
See public/models/README.md for detailed troubleshooting steps.
📚 Documentation
For complete documentation, see:
docs/components/3D_VIEWER_REFACTOR_TDD.md- Refactor design documentdocs/components/3D_ARCHITECTURE.md- System architecture overviewdocs/components/BLENDER_WORKFLOW.md- Model export workflow
Using in MDX Content
To use the 3D viewer in MDX comic pages or blog posts, you need to import it at the top of your MDX file.
Simple Props-Based Usage (Recommended for MDX)
---
title: "My 3D Model Post"
# ... other frontmatter
---
import ModelViewer from '@components/react/3d';
# Check Out This 3D Model!
Here's my character model with auto-rotate and cel-shading:
<ModelViewer
client:load
modelPath="/models/my-character.glb"
height="600px"
autoRotate={true}
toonShading={true}
showControls={true}
/>
Pretty cool, right? Compound Components in MDX (Advanced)
For compound components, create a React wrapper component first:
// src/components/react/CharacterViewer.tsx
import ModelViewer, {
ViewerCanvas,
ViewerControls,
ViewControl,
CameraControl,
AnimationControl
} from '@components/react/3d';
export const CharacterViewer = ({ modelPath }: { modelPath: string }) => (
<ModelViewer modelPath={modelPath}>
<ViewerCanvas height="700px" />
<ViewerControls title="Character Controls">
<ViewControl.AutoRotate />
<ViewControl.ToonShading />
<CameraControl.PresetList />
<AnimationControl.Player />
</ViewerControls>
</ModelViewer>
); Then use it in your MDX:
---
title: "Character Showcase"
---
import { CharacterViewer } from '@components/react/CharacterViewer';
# Meet My Character
<CharacterViewer
client:load
modelPath="/models/my-character.glb"
/> Available Props for MDX Usage
| Prop | Type | Default | Description |
|---|---|---|---|
modelPath | string | required | Path to GLB/GLTF/FBX model |
height | string | "600px" | Canvas height |
autoRotate | boolean | false | Auto-rotate model |
autoRotateSpeed | number | 1.0 | Rotation speed (0.1-5.0) |
toonShading | boolean | false | Enable cel-shading |
showControls | boolean | true | Show control panel |
hideControls | string[] | [] | Array of control IDs to hide |
Tips for MDX Usage
- ✅ Always use
client:loaddirective for interactivity - ✅ Place models in
public/models/for easy paths - ✅ Use props-based API for simple embedding
- ✅ Create wrapper components for custom compound layouts
- ✅ Test in both light and dark mode (viewer is theme-aware)
- ⚠️ GLB models with Blender cameras will have preset buttons automatically