@heliguy-xyz/splat-viewer
v1.0.0-rc.17
Published
A standalone 3D Gaussian Splat viewer web component with multi-model support, transform system, fly camera, and scene configuration
Readme
Heliguy Web Viewer
A self-contained 3D Gaussian Splat viewer web component built with PlayCanvas for high-performance rendering of point cloud data. Framework-agnostic and embeddable in any web application with zero external dependencies.
Features
- 🎯 Gaussian Splat Rendering: Native support for
.splatfiles using PlayCanvas - 📊 PLY Support: Traditional point cloud rendering for
.plyand.ply.compressedformats - 📦 SOG Support: Native support for
.sog(Spatially Ordered Gaussians) files - 🎮 Interactive Controls: Smooth camera navigation and manipulation
- 🧭 Navigation Cube: Interactive 3D cube for quick camera orientation control
- 📈 Performance Monitoring: Real-time rendering statistics
- ⚡ High Performance: WebGL2-powered rendering with PlayCanvas engine
- 📱 Responsive Design: Full-screen immersive viewing experience
- 🔧 Self-Contained: Single file with bundled PlayCanvas engine (2MB)
- 🎨 Built-in Loading: Professional loading spinner with orange/black theme
- 🚀 Zero Dependencies: No external scripts required
Enhanced Features (v1.0.0)
- 🎭 Multi-Model Support: Load and manage multiple models in the same scene
- 🔄 Transform System: Programmatically position, rotate, and scale models
- 🎥 Fly Camera Mode: First-person WASD + mouse-look navigation with configurable controls
- 🎨 Scene Configuration: Adjust FOV and background color with smooth animations
- 📡 Comprehensive Events: React to model lifecycle, transforms, camera changes, and more
- 📘 TypeScript Support: Full type definitions for all APIs
Quick Start
Installation
Via npm (Recommended)
npm install @heliguy/web-viewerVia CDN
<!-- UMD Bundle -->
<script src="https://unpkg.com/@heliguy/[email protected]/dist/web-component/splat-viewer.min.js"></script>
<!-- ESM Bundle -->
<script type="module">
import '@heliguy/web-viewer/esm'
</script>Prerequisites
- Modern browser with WebGL2 support
- Node.js ≥18.0.0 (for development only)
Usage
Vanilla HTML
<!DOCTYPE html>
<html>
<head>
<title>3D Splat Viewer</title>
</head>
<body>
<splat-viewer
id="viewer"
width="100%"
height="600px"
auto-focus
enable-navigation-cube
></splat-viewer>
<!-- Import from npm package -->
<script src="node_modules/@heliguy/web-viewer/dist/web-component/splat-viewer.min.js"></script>
<script>
const viewer = document.getElementById('viewer')
viewer.addEventListener('ready', () => {
viewer.load('path/to/model.splat')
})
</script>
</body>
</html>Embed via iframe
Use the provided viewer.html page that reads query parameters and configures the web component.
Host viewer.html alongside dist/web-component/splat-viewer.min.js.
Supported query params:
src(required): URL to your model (encode it!)width(optional): CSS width (e.g.100%,800px)height(optional): CSS height (e.g.100vh,600px)autoFocus(optional):true|false(default:true)stats(optional):true|false
Example:
<iframe
src="https://your-domain.com/viewer.html?src=https%3A%2F%2Fcdn.yourdomain.com%2Fmodels%2FRyhope.ply&width=100%25&height=100vh&autoFocus=true"
width="100%"
height="600"
style="border:0; background:#000"
allowfullscreen
></iframe>Notes:
- Ensure CORS allows the viewer origin to fetch the model (
Access-Control-Allow-Origin). - Prefer hosting
viewer.html, the bundle, and models on the same domain to avoid CORS. - URL-encode the
srcvalue.
React / Next.js
import { useEffect, useRef } from 'react'
import '@heliguy/web-viewer'
function App() {
const viewerRef = useRef<any>(null)
useEffect(() => {
const viewer = viewerRef.current
if (!viewer) return
const handleReady = () => {
viewer.load('/models/scene.splat')
}
viewer.addEventListener('ready', handleReady)
return () => viewer.removeEventListener('ready', handleReady)
}, [])
return (
<splat-viewer
ref={viewerRef}
width="100%"
height="600px"
auto-focus
/>
)
}TypeScript: Add to your global.d.ts:
declare namespace JSX {
interface IntrinsicElements {
'splat-viewer': React.DetailedHTMLProps<
React.HTMLAttributes<HTMLElement> & {
ref?: React.Ref<any>
src?: string
width?: string
height?: string
'auto-focus'?: boolean
'enable-stats'?: boolean
},
HTMLElement
>
}
}Vue 3
<template>
<splat-viewer
ref="viewerRef"
width="100%"
height="600px"
auto-focus
/>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import '@heliguy/web-viewer'
const viewerRef = ref(null)
onMounted(() => {
const viewer = viewerRef.value
const handleReady = () => {
viewer.load('/models/scene.splat')
}
viewer.addEventListener('ready', handleReady)
})
</script>Commands
| Command | Description |
| ----------------------------- | ---------------------------------- |
| npm run web-component:serve | Start development server |
| npm run web-component:build | Build self-contained web component |
| npm run web-component:clean | Clean build artifacts |
| npm run lint | Check code quality with ESLint |
| npm run lint:fix | Fix linting issues automatically |
| npm run format | Format code with Prettier |
| npm run format:check | Check code formatting |
Web Component API
Attributes
Basic Configuration
src- Model file path or URLwidth- Canvas width (default: "100%")height- Canvas height (default: "400px")auto-focus- Auto-focus on load (default: true)enable-stats- Show performance stats (default: false)max-splats- Maximum splat count (default: 2000000)
Camera Controls
orbit-sensitivity- Mouse orbit sensitivity (default: 0.3)pan-sensitivity- Mouse pan sensitivity (default: 0.5)zoom-sensitivity- Mouse zoom sensitivity (default: 0.1)min-distance- Minimum camera distance (default: 1)max-distance- Maximum camera distance (default: 100)
Navigation Cube
enable-navigation-cube- Show interactive navigation cube (default: false)navigation-cube-size- Cube size in pixels (default: 120)navigation-cube-transition- Camera transition duration in ms (default: 800)
Events
Core Events
ready- Component initializedloading-start- Model loading startedloading-progress- Model loading progress updateloaded- Model loaded successfullyerror- Loading error occurredstats-update- Performance statistics updatecamera-change- Camera position/target changedinteraction-start/interaction-end- User interaction with orbit controls
Multi-Model Events (v1.0.0)
model-added- Model added to scenemodel-removed- Model removed from scenescene-cleared- All models cleared
Transform Events (v1.0.0)
model-transform-changed- Model transform updated
Camera Events (v1.0.0)
camera-mode-changed- Camera mode switched (orbit/fly)fly-camera-move- Fly camera position changedfly-camera-look- Fly camera rotation changed
Scene Config Events (v1.0.0)
camera-fov-changed- Camera FOV changedbackground-color-changed- Background color changed
Project Structure
src/
├── web-component/ # Main web component implementation
│ ├── SplatViewerElement.ts # Web component class
│ ├── SplatViewerCore.ts # Core rendering engine
│ ├── NavigationCubeController.ts # Navigation cube controller
│ ├── OrbitCameraScript.ts # Camera controls
│ ├── navigation-cube/ # Navigation cube modules
│ │ ├── CubeRenderer.ts # 3D cube visual rendering
│ │ ├── ControlPointManager.ts # Control point UI elements
│ │ └── CameraTransitionController.ts # Camera animations
│ ├── types/ # TypeScript definitions
│ ├── loader/ # File loading system (.ply, .sog, .splat)
│ └── utils/ # Utility functions
├── build/ # Build configuration
├── dist/web-component/ # Built self-contained web component
│ └── splat-viewer.min.js # Single 2MB file with PlayCanvas
├── test-web-component.html # Basic test page
└── test-navigation-cube.html # Navigation cube test page
assets/ # 3D model assets for testing
docs/ # DocumentationSupported File Formats
- .sog - Spatially Ordered Gaussians (PlayCanvas native support)
- .splat - Gaussian Splat files (PlayCanvas native support)
- .ply - Point cloud files (standard format)
- .ply.compressed - Compressed PLY files (gzip/deflate)
Self-Contained Bundle
The web component is distributed as a single 2MB file (splat-viewer.min.js) that includes:
- Complete PlayCanvas engine (v2.11.8)
- Web component implementation
- File format loaders
- Performance monitoring
- Interactive controls
Benefits:
- Zero external dependencies
- Version consistency guaranteed
- Easy embedding in any web application
- Offline functionality
Development
# Start development server
npm run web-component:serve
# Build for production
npm run web-component:build
# Clean build artifacts
npm run web-component:cleanDeployment
Netlify
- Publish directory:
dist - Build command:
npm run build - Node version: 18 (configured in
netlify.toml)
The build emits dist/index.html from viewer.html using a small zero-dependency Node script
(build/copy-viewer-to-index.mjs). Rollup uses an ESM config at build/rollup.config.mjs.
License
MIT License - see LICENSE file for details.
