A lightweight, interactive 3D model viewer for web applications with full TypeScript support. Drop it into any HTML element and display interactive 3D models with professional lighting and controls.
✅ Full TypeScript Support - Complete type definitions and IntelliSense
✅ Multiple Formats - ESM and CommonJS compatibility
✅ Professional Lighting - HDR environment maps and dynamic sun positioning
✅ Interactive Controls - Built-in orbit controls and model framing
✅ Edge Rendering - Conditional and threshold edge detection for technical visualization
✅ Framework Agnostic - Works with React, Vue, Angular, or vanilla JavaScript
✅ Mobile Optimized - Responsive design with touch controls
npm install @mauc/glimmerCreate a container element in your HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My 3D Viewer</title>
</head>
<body>
<div id="model-viewer"></div>
<script type="module" src="main.js"></script>
</body>
</html>import { Glimmer } from '@mauc/glimmer'
// Get your container element
const container = document.getElementById('model-viewer')!
// Create the viewer with your 3D model
const viewer = new Glimmer(container, {
models: [{
url: '/path/to/your-model.glb'
}]
})
// Initialize and you're done!
await viewer.initialize()That's it! Your website now has a fully interactive 3D viewer with complete TypeScript support.
import { Glimmer, type GlimmerSettings, type ModelSettings } from '@mauc/glimmer'
// Fully typed configuration
const config: GlimmerSettings = {
environment: {
type: 'hdr',
url: '/studio.hdr',
transparent: false
},
models: [{
url: '/model.glb',
color: 0xff0000,
shadow: true,
conditional_edges: {
color: 0x000000,
line_width: 2
}
}] satisfies ModelSettings[]
}
const viewer = new Glimmer(container, config)
await viewer.initialize()import type {
GlimmerSettings,
ModelSettings,
EnvironmentSettings,
SunSettings,
EdgeSettings
} from '@mauc/glimmer'import React, { useEffect, useRef } from 'react'
import { Glimmer } from '@mauc/glimmer'
interface ModelViewerProps {
modelUrl: string
width?: string
height?: string
}
export function ModelViewer({ modelUrl, width = '100%', height = '400px' }: ModelViewerProps) {
const containerRef = useRef<HTMLDivElement>(null)
const glimmerRef = useRef<Glimmer>()
useEffect(() => {
if (!containerRef.current) return
// Create viewer
glimmerRef.current = new Glimmer(containerRef.current, {
models: [{ url: modelUrl }],
environment: { type: 'sky' }
})
glimmerRef.current.initialize()
// Cleanup on unmount
return () => {
glimmerRef.current?.dispose()
}
}, [modelUrl])
return (
<div
ref={containerRef}
style={{ width, height }}
className="model-viewer"
/>
)
}
// Usage in your React app
function ProductPage() {
return (
<div>
<h1>Our Latest Product</h1>
<ModelViewer
modelUrl="/products/chair.glb"
height="500px"
/>
</div>
)
}<template>
<div
ref="container"
class="model-viewer"
:style="{ width, height }"
/>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import { Glimmer } from '@mauc/glimmer'
interface Props {
modelUrl: string
width?: string
height?: string
}
const props = withDefaults(defineProps<Props>(), {
width: '100%',
height: '400px'
})
const container = ref<HTMLDivElement>()
let glimmer: Glimmer
onMounted(async () => {
if (!container.value) return
glimmer = new Glimmer(container.value, {
models: [{ url: props.modelUrl }],
environment: { type: 'hdr', url: '/lighting.hdr' }
})
await glimmer.initialize()
})
onUnmounted(() => {
glimmer?.dispose()
})
</script>
<style scoped>
.model-viewer {
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
</style><!DOCTYPE html>
<html>
<head>
<style>
.viewer {
width: 100%;
height: 500px;
border: 1px solid #ddd;
border-radius: 8px;
}
</style>
</head>
<body>
<div id="viewer" class="viewer"></div>
<script type="module">
import { Glimmer } from '@mauc/glimmer'
const container = document.getElementById('viewer')
const glimmer = new Glimmer(container, {
environment: {
type: 'sky',
sun: { intensity: 1.5 }
},
models: [{
url: './models/demo.glb',
shadow: true
}]
})
glimmer.initialize().then(() => {
console.log('3D viewer ready!')
})
</script>
</body>
</html>const glimmer = new Glimmer(container, {
environment: {
type: 'hdr',
url: '/studio-lighting.hdr'
},
models: [
{
url: '/models/chair.glb',
color: 0x8B4513 // Brown chair
},
{
url: '/models/table.glb',
color: 0x654321 // Dark brown table
}
]
})
await glimmer.initialize()// Start with empty viewer
const glimmer = new Glimmer(container)
await glimmer.initialize()
// Load models dynamically based on user selection
function showProduct(productId) {
const modelUrl = `/products/${productId}.glb`
glimmer.model_manager.clear_all()
glimmer.model_manager.load_model(modelUrl)
}
// Example usage
document.getElementById('product-1').onclick = () => showProduct('chair')
document.getElementById('product-2').onclick = () => showProduct('table')const glimmer = new Glimmer(container, {
environment: {
type: 'hdr',
url: '/assets/studio.hdr', // Professional product lighting
transparent: false
},
models: [{
url: '/products/item.glb',
shadow: true,
color: 0xffffff // Let original materials show
}]
})const glimmer = new Glimmer(container, {
environment: {
type: 'sky',
sun: {
intensity: 2.0,
position: [100, 100, 50], // Natural daylight
shadow: true
}
},
models: [{
url: '/buildings/house.glb',
threshold_edges: {
color: 0x333333, // Architectural edge lines
threshold_angle: 30,
line_width: 1
}
}]
})const glimmer = new Glimmer(container, {
environment: {
type: 'none',
transparent: true, // Clean background
background_color: 0xffffff
},
models: [{
url: '/technical/part.glb',
conditional_edges: {
color: 0x000000, // Technical drawing style
line_width: 2,
threshold_angle: 1
}
}]
}).model-viewer {
width: 100%;
height: 400px;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0,0,0,0.15);
overflow: hidden;
background: #f5f5f5;
}
/* Responsive sizing */
@media (max-width: 768px) {
.model-viewer {
height: 300px;
}
}
/* Full screen viewer */
.fullscreen-viewer {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 1000;
}<div class="product-grid">
<div class="product-info">
<h2>Product Name</h2>
<p>Description...</p>
<button>Add to Cart</button>
</div>
<div class="model-viewer-container">
<div id="viewer"></div>
</div>
</div>
<style>
.product-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
align-items: center;
}
@media (max-width: 768px) {
.product-grid {
grid-template-columns: 1fr;
}
}
</style>const glimmer = new Glimmer(container, {
models: [{ url: '/model.glb' }]
})
await glimmer.initialize()
// Add custom buttons
document.getElementById('zoom-in').onclick = () => {
// Glimmer uses OrbitControls - zoom programmatically
glimmer.viewer.controls.dollyIn(1.2)
glimmer.viewer.controls.update()
}
document.getElementById('reset-view').onclick = () => {
glimmer.viewer.frame_model() // Reset to default view
}
document.getElementById('animate').onclick = () => {
glimmer.viewer.animate_orbit(true) // Start rotation
}// Show loading indicator
const loadingDiv = document.getElementById('loading')
loadingDiv.style.display = 'block'
try {
const glimmer = new Glimmer(container, {
models: [{ url: '/large-model.glb' }]
})
await glimmer.initialize()
loadingDiv.style.display = 'none'
} catch (error) {
loadingDiv.innerHTML = 'Error loading 3D model'
console.error('Failed to load model:', error)
}const glimmer = new Glimmer(container, {
environment: {
type: 'color', // Simpler than HDR on mobile
background_color: 0xf0f0f0
},
models: [{
url: '/models/optimized.glb', // Use lower-poly models
shadow: window.innerWidth > 768 // Disable shadows on mobile
}]
})// Clean up when navigating away or hiding viewer
window.addEventListener('beforeunload', () => {
glimmer.dispose()
})
// Or for Single Page Applications
function hideViewer() {
glimmer.dispose()
glimmer = null
}- glTF 2.0 (.glb recommended) - Best performance and compatibility
- HDR environment maps for professional lighting
- Keep models under 10MB for web performance
- Use glTF 2.0 binary format (.glb) for fastest loading
- Optimize textures (1024px max recommended for web)
- Use Draco compression when possible
- Test on mobile devices for performance validation
- Chrome 80+ ✅ Full support
- Firefox 75+ ✅ Full support
- Safari 13+ ✅ Full support
- Edge 80+ ✅ Full support
- Mobile browsers ✅ Touch controls included
Requires WebGL 2.0 support
// Check if model URL is accessible
try {
const response = await fetch('/path/to/model.glb')
if (!response.ok) {
console.error('Model file not found:', response.status)
}
} catch (error) {
console.error('Network error:', error)
}Ensure you have the correct import paths:
// ✅ Correct
import { Glimmer } from '@mauc/glimmer'
// ❌ Incorrect
import { Glimmer } from '@mauc/glimmer/dist/index.js'Ensure your 3D models are served from the same domain or with proper CORS headers:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET
- Reduce model complexity and polygon count
- Disable shadows on mobile devices
- Use simpler lighting (type: 'color' instead of 'hdr')
- Consider model optimization tools like gltfpack
@mauc/glimmer@1.0.8
├── dist/
│ ├── index.d.ts # Main TypeScript declarations
│ ├── vite/
│ │ ├── glimmer.mjs # ESM build
│ │ └── glimmer.umd.js # CommonJS/UMD build
│ └── **/*.d.ts # Complete type definitions
├── LICENSE
└── README.md
The package supports both ESM and CommonJS with complete TypeScript declarations.
- ✅ Complete TypeScript support with full type definitions
- ✅ Dual package format (ESM + CommonJS)
- ✅ Comprehensive test suite (48 tests)
- ✅ Improved build process and development workflow
- ✅ Enhanced documentation and examples
Note: Public repository and examples will be available when the package is published to npm.
This project is licensed under the MIT License - see the LICENSE file for details.
Third-Party Dependencies: This software uses Three.js and other open-source libraries. Users are responsible for compliance with all dependency licenses.
Asset Rights: Users must ensure they have proper rights to any 3D models, textures, or environment maps used with this library. The example assets in this repository are for demonstration only.
Browser Compatibility: While we support modern browsers, WebGL performance and compatibility vary across devices. Test thoroughly on your target platforms.
Performance: 3D rendering performance depends on device capabilities, model complexity, and browser implementation. No performance guarantees are provided.
Production Use: This library may be used in production, but users assume all responsibility for testing and validation in their specific environments.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
See CONTRIBUTING.md for detailed guidelines.
Ready to add 3D to your website? Start with the basic example above!