Skip to content

Hosting 4DGS Content

Guide to hosting 4DGS frame sequences for web playback.

Overview

4DGS content consists of many frame files that need to be:

  1. Hosted on a web-accessible server
  2. Served with proper CORS headers
  3. 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

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:

  1. Create CloudFront distribution
  2. Set S3 bucket as origin
  3. Enable CORS forwarding
  4. 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

  1. Enable HTTP/2 or HTTP/3 - Multiplexed connections
  2. Set long cache times - Frames don't change
  3. Use compression - SOG files compress well
  4. Geographic distribution - Edge locations near users

Cache Headers

Cache-Control: public, max-age=31536000, immutable

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

  1. Verify CORS headers are set on your CDN
  2. Check preflight (OPTIONS) requests work
  3. Test with curl -I to see headers
curl -I https://cdn.example.com/4dgs/video/frames/frame_000.sog

Frames loading slowly

  1. Check CDN edge locations
  2. Verify compression is enabled
  3. Consider SOG format (smaller files)
  4. Increase preloadCount

Playback stuttering

  1. Reduce FPS
  2. Increase preloadCount
  3. Check client bandwidth
  4. Use smaller frame files (lower quality)

404 errors on frames

  1. Verify all frames uploaded
  2. Check URL patterns match
  3. 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