Hosting 4DGS Content
Guide to hosting 4DGS frame sequences for web playback.
Overview
4DGS content consists of many frame files that need to be:
- Hosted on a web-accessible server
- Served with proper CORS headers
- Delivered with low latency for smooth playback
Hosting Requirements
Essential Requirements
| Requirement | Description |
|---|---|
| CORS Headers | Must allow cross-origin requests |
| HTTPS | Required for modern browsers |
| Bandwidth | Sufficient for streaming frames |
| Latency | Low latency for smooth playback |
CORS Configuration
Your server must return these headers:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, HEAD, OPTIONS
Access-Control-Allow-Headers: Content-Type, Range
Access-Control-Expose-Headers: Content-Length, Content-Range
Cloud Storage Options
Cloudflare R2 (Recommended)
Pros: Free egress, global CDN, great performance
# Install rclone
brew install rclone # or appropriate package manager
# Configure R2
rclone config
# Follow prompts to add Cloudflare R2
# Upload frames
rclone sync ./frames r2:my-bucket/4dgs/my-video/
CORS Configuration (R2 Dashboard):
[
{
"AllowedOrigins": ["*"],
"AllowedMethods": ["GET", "HEAD"],
"AllowedHeaders": ["*"],
"MaxAgeSeconds": 3600
}
]
AWS S3 + CloudFront
Pros: Reliable, scalable, enterprise-ready
# Upload to S3
aws s3 sync ./frames s3://my-bucket/4dgs/my-video/
# Set public read (or use CloudFront)
aws s3api put-bucket-policy --bucket my-bucket --policy file://policy.json
CORS Configuration (S3 Console):
[
{
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET", "HEAD"],
"AllowedOrigins": ["*"],
"ExposeHeaders": ["Content-Length", "Content-Range"],
"MaxAgeSeconds": 3600
}
]
CloudFront Setup:
- Create CloudFront distribution
- Set S3 bucket as origin
- Enable CORS forwarding
- Use distribution URL in your app
Google Cloud Storage
Pros: Good integration with GCP services
# Upload frames
gsutil -m cp -r ./frames gs://my-bucket/4dgs/my-video/
# Set CORS
gsutil cors set cors.json gs://my-bucket
cors.json:
[
{
"origin": ["*"],
"method": ["GET", "HEAD"],
"responseHeader": ["Content-Type", "Content-Length", "Content-Range"],
"maxAgeSeconds": 3600
}
]
Vercel Blob
Pros: Easy setup, good for smaller projects
import { put } from '@vercel/blob';
import fs from 'fs';
import path from 'path';
async function uploadFrames(framesDir: string, prefix: string) {
const files = fs.readdirSync(framesDir);
const urls = [];
for (const file of files) {
const filePath = path.join(framesDir, file);
const blob = await put(`${prefix}/${file}`, fs.readFileSync(filePath), {
access: 'public',
});
urls.push(blob.url);
}
return urls;
}
Self-Hosted (nginx)
Pros: Full control, no third-party dependencies
nginx.conf:
server {
listen 443 ssl;
server_name cdn.example.com;
# SSL configuration
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# 4DGS frames location
location /4dgs/ {
alias /var/www/4dgs/;
# CORS headers
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, HEAD, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type, Range' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length, Content-Range' always;
# Handle preflight
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
# Caching
expires 1y;
add_header Cache-Control "public, immutable";
# Compression
gzip on;
gzip_types application/octet-stream;
}
}
Directory Structure
Organize your 4DGS content:
cdn.example.com/
└── 4dgs/
├── video-1/
│ ├── manifest.json
│ └── frames/
│ ├── frame_000.sog
│ ├── frame_001.sog
│ └── ...
└── video-2/
├── manifest.json
└── frames/
└── ...
Using Hosted Content
With Manifest
// Fetch manifest
const response = await fetch('https://cdn.example.com/4dgs/video-1/manifest.json');
const manifest = await response.json();
// Build frame URLs
const baseUrl = 'https://cdn.example.com/4dgs/video-1/frames/';
const frameUrls = manifest.frames.map(f => baseUrl + f);
// Create viewer
const viewer = createViewer(container, {
frameSequence: {
frameUrls,
fps: manifest.fps,
loop: manifest.loop,
},
});
Direct Frame URLs
// Generate URLs programmatically
const baseUrl = 'https://cdn.example.com/4dgs/video-1/frames/';
const frameCount = 120;
const frameUrls = Array.from(
{ length: frameCount },
(_, i) => `${baseUrl}frame_${String(i).padStart(3, '0')}.sog`
);
const viewer = createViewer(container, {
frameSequence: {
frameUrls,
fps: 24,
loop: true,
},
});
Performance Optimization
CDN Configuration
- Enable HTTP/2 or HTTP/3 - Multiplexed connections
- Set long cache times - Frames don't change
- Use compression - SOG files compress well
- Geographic distribution - Edge locations near users
Cache Headers
Preloading Strategy
// Viewer handles preloading automatically
const viewer = createViewer(container, {
frameSequence: {
frameUrls,
fps: 24,
preloadCount: 10, // Preload 10 frames ahead
},
});
Adjust preloadCount based on:
| Scenario | preloadCount |
|---|---|
| Fast CDN, good bandwidth | 15-20 |
| Average connection | 5-10 |
| Mobile / slow connection | 3-5 |
Bandwidth Estimation
Calculate required bandwidth:
Bandwidth = (average frame size) × FPS
Example:
- Frame size: 2 MB
- FPS: 24
- Required: 48 MB/s = 384 Mbps
With preloading:
- Preload allows buffering
- Effective requirement reduced by ~50%
Monitoring
Track Loading Performance
viewer.on('progress', ({ progress, text }) => {
console.log(`Loading: ${(progress * 100).toFixed(0)}%`);
// Send to analytics
analytics.track('4dgs_load_progress', { progress });
});
viewer.on('ready', () => {
analytics.track('4dgs_ready', {
loadTime: performance.now() - startTime,
});
});
CDN Analytics
Monitor in your CDN dashboard:
- Request count per frame
- Bandwidth usage
- Cache hit ratio
- Geographic distribution
- Error rates
Troubleshooting
"CORS error" in console
- Verify CORS headers are set on your CDN
- Check preflight (OPTIONS) requests work
- Test with
curl -Ito see headers
Frames loading slowly
- Check CDN edge locations
- Verify compression is enabled
- Consider SOG format (smaller files)
- Increase preloadCount
Playback stuttering
- Reduce FPS
- Increase preloadCount
- Check client bandwidth
- Use smaller frame files (lower quality)
404 errors on frames
- Verify all frames uploaded
- Check URL patterns match
- Verify file naming (padding: frame_000 vs frame_0)
Cost Estimation
Storage Costs
| Duration | FPS | Frames | Size (SOG) | Monthly Storage* |
|---|---|---|---|---|
| 5 sec | 24 | 120 | ~120 MB | ~$0.03 |
| 30 sec | 24 | 720 | ~720 MB | ~$0.15 |
| 1 min | 24 | 1440 | ~1.4 GB | ~$0.30 |
*Based on typical cloud storage pricing (~$0.02/GB/month)
Bandwidth Costs
| Views/Month | Duration | Bandwidth | Cost (R2)* | Cost (S3)** |
|---|---|---|---|---|
| 1,000 | 5 sec | 120 GB | $0 | ~$10 |
| 10,000 | 5 sec | 1.2 TB | $0 | ~$100 |
| 100,000 | 5 sec | 12 TB | $0 | ~$1,000 |
Cloudflare R2: Free egress *AWS S3 + CloudFront: ~$0.085/GB
Next Steps
- Custom Integrations - Advanced viewer customization
- 4DGS Playback - Viewer configuration
- Video to 4DGS - Creating content