@stereos/sdk
v0.1.3
Published
Client SDK for Stereos - Convert Gaussian splats to glTF
Maintainers
Readme
@stereos/sdk
Client SDK for Stereos — Convert 3D Gaussian splats to optimized glTF/glb format entirely in the browser using WebAssembly.
Features
- 🚀 Browser-native processing — All conversion happens client-side via WebAssembly
- 📦 Zero dependencies — Lightweight SDK with no external runtime dependencies
- 🔐 Secure token-based auth — Short-lived JWTs for safe API access
- 🧹 Built-in cleaning — Remove low-quality splats automatically
- 🗜️ Advanced compression — Position quantization and meshopt compression support
- 🎨 View-dependent rendering — Optional full spherical harmonics export
Installation
npm install @stereos/sdkQuick Start
import { Stereos } from '@stereos/sdk';
// Initialize with your API key
const stereos = new Stereos({ apiKey: 'sk_live_...' });
// Convert a PLY file to glb
const result = await stereos.convert(plyFile);
// Download the result
Stereos.download(result);Architecture
Overview
┌─────────────────────────────────────────────────────────────┐
│ Your Application │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
│ │ Stereos │───▶│ WASM Core │───▶│ glTF/glb Output │ │
│ │ Client │ │ (stereos-wasm)│ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ Token API │ (Secure short-lived JWT authentication) │
│ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘Components
1. Stereos Client (src/client.ts)
The main SDK class that orchestrates:
- Token management: Automatically fetches and caches JWT tokens from the API
- WASM loading: Lazy-loads the WebAssembly module on first use
- Conversion pipeline: Handles file reading, WASM execution, and result formatting
class Stereos {
private apiKey: string;
private wasm: WasmModule | null;
private token: string | null;
async convert(file, options): Promise<ConvertResult>
async validateToken(): Promise<TokenClaims>
static download(result): void
}2. WASM Module (wasm/)
Compiled from Rust (crates/stereos-wasm) using wasm-bindgen:
- Core processing: PLY parsing, cleaning algorithms, glTF generation
- Zero-copy: Efficient memory sharing between JS and WASM
- Streaming: Supports incremental processing of large files
3. Types (src/types.ts)
TypeScript interfaces for:
- Configuration options
- API responses
- Conversion results
- Cleaning statistics
API Reference
Stereos Class
Constructor
new Stereos(options: StereosOptions): Stereos| Option | Type | Required | Description |
|--------|------|----------|-------------|
| apiKey | string | ✅ | Your Stereos API key |
Methods
convert(file, options?)
Convert a PLY file to glTF/glb format.
async convert(
file: File | Blob | ArrayBuffer | Uint8Array,
options?: ConvertOptions
): Promise<ConvertResult>ConvertOptions:
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| format | 'glb' \| 'gltf' | 'glb' | Output format |
| quantizeColors | boolean | true | Quantize colors to u8 |
| exportFullSh | boolean | false | Export all 48 SH coefficients |
| quantizePositions | boolean | false | Quantize positions to i16 |
| meshoptCompression | boolean | false | Enable meshopt compression |
| clean | boolean \| CleanOptions | false | Enable cleaning/filtering |
CleanOptions (when clean is an object):
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| minOpacity | number | 0.005 | Remove splats below this opacity |
| minScale | number | 0.0001 | Remove splats smaller than this |
| outlierSigma | number | — | Remove outliers N std devs from centroid |
validateToken()
Validate the current token and return its claims.
async validateToken(): Promise<TokenClaims>Returns:
interface TokenClaims {
sub: string; // API key ID
exp: number; // Expiration timestamp
iat: number; // Issued at timestamp
conversions_remaining: number;
max_file_size: number;
formats: string[];
}version()
Get the SDK/WASM version.
async version(): Promise<string>Stereos.download(result) (static)
Trigger a browser download of the conversion result.
static download(result: ConvertResult): voidStereos.createDownloadUrl(result) (static)
Create a blob URL for the conversion result.
static createDownloadUrl(result: ConvertResult): stringTypes Reference
Core Types
StereosOptions
interface StereosOptions {
/** Your API key */
apiKey: string;
}ConvertResult
interface ConvertResult {
/** The converted file data */
data: Uint8Array;
/** Output format */
format: "glb" | "gltf";
/** Suggested filename */
filename: string;
/** Cleaning statistics (if cleaning was enabled) */
cleanStats?: CleanStats;
}CleanStats
interface CleanStats {
/** Number of splats before cleaning */
original_count: number;
/** Number of splats removed due to low opacity */
removed_low_opacity: number;
/** Number of splats removed due to small scale */
removed_small_scale: number;
/** Number of splats removed as outliers */
removed_outliers: number;
/** Number of splats after cleaning */
final_count: number;
}ApiError
interface ApiError {
error: string; // Error code
message?: string; // Human-readable message
}Usage Examples
Basic Conversion
import { Stereos } from '@stereos/sdk';
const stereos = new Stereos({ apiKey: 'sk_live_...' });
const result = await stereos.convert(plyFile);
Stereos.download(result);With File Input
const fileInput = document.getElementById('file');
fileInput.addEventListener('change', async (e) => {
const file = e.target.files[0];
const result = await stereos.convert(file, { format: 'glb' });
Stereos.download(result);
});Maximum Compression
Best for web delivery — smallest file size:
const result = await stereos.convert(file, {
format: 'glb',
quantizeColors: true, // u8 colors (default)
quantizePositions: true, // i16 positions (~2x smaller)
clean: true, // Remove low-quality splats
exportFullSh: false, // DC colors only
meshoptCompression: true, // Additional 2-4x compression
});Full Quality Export
For view-dependent rendering with full spherical harmonics:
const result = await stereos.convert(file, {
format: 'glb',
quantizeColors: false, // Keep f32 precision
exportFullSh: true, // All 48 SH coefficients
clean: {
minOpacity: 0.001, // Keep more splats
minScale: 0.00005,
},
});Cleaning with Custom Options
const result = await stereos.convert(file, {
clean: {
minOpacity: 0.01, // Aggressive opacity filtering
minScale: 0.001, // Remove tiny splats
outlierSigma: 3, // Remove statistical outliers
},
});
if (result.cleanStats) {
const { original_count, final_count, removed_outliers } = result.cleanStats;
console.log(`Cleaned: ${original_count} → ${final_count} splats`);
console.log(`Removed ${removed_outliers} outliers`);
}Check Token Status
const claims = await stereos.validateToken();
console.log(`Conversions remaining: ${claims.conversions_remaining}`);
console.log(`Max file size: ${claims.max_file_size} bytes`);Error Handling
try {
const result = await stereos.convert(file);
} catch (error) {
if (error.message.includes('401')) {
console.error('Invalid API key');
} else if (error.message.includes('quota')) {
console.error('Out of conversions');
} else {
console.error('Conversion failed:', error.message);
}
}Testing
All tests are currently passing ✅
Run tests with: cargo test -p stereos-core
Test Files
crates/stereos-core/src/lib.rs
Core library tests:
test_gaussian_cloud_capacity— Validates GaussianCloud capacity calculationstest_export_config_default— Tests default ExportConfig values
crates/stereos-core/src/token.rs
JWT token validation tests:
test_claims_allows_format— Tests format permission checking in claimstest_validate_invalid_key— Tests invalid API key rejection (native only)
crates/stereos-core/src/ply.rs
PLY file parsing tests:
test_sigmoid— Tests sigmoid activation functiontest_parse_vertex_count— Tests vertex count extraction from PLY headertest_parse_vertex_count_missing— Tests handling of missing element vertex
crates/stereos-core/src/clean.rs
Cleaning/filtering algorithm tests:
test_clean_removes_low_opacity_splats— Opacity threshold filteringtest_clean_keeps_splats_at_opacity_threshold— Boundary condition handlingtest_clean_removes_small_scale_splats— Scale-based filteringtest_clean_keeps_splat_if_any_scale_dimension_large_enough— Multi-dimensional scale checktest_clean_removes_outliers_by_sigma— Statistical outlier removaltest_clean_no_outlier_removal_when_sigma_none— Optional outlier skippingtest_clean_applies_all_filters— Combined filter applicationtest_clean_stats_count_each_removal_reason_once— Statistics accuracytest_clean_empty_cloud— Empty input handlingtest_clean_preserves_all_attributes— Data preservation verificationtest_clean_with_default_options— Default options behavior
crates/stereos-core/src/gltf.rs
glTF/glb export tests:
test_sh_to_rgba— Spherical harmonics to RGBA conversiontest_sh_to_rgba_with_values— SH conversion with specific valuestest_export_glb_structure— GLB binary structure validationtest_export_glb_with_stats_structure— GLB with embedded statstest_export_gltf_embedded— Embedded glTF JSON exporttest_compute_bounds— Bounding box calculationstest_compute_bounds_empty— Empty cloud bounds handlingtest_compute_bounds_single_point— Single point boundstest_export_with_full_sh— Full spherical harmonics export (48 coefficients)test_export_dc_only_no_sh_attribute— Diffuse color only exporttest_export_with_position_quantization— Position quantization to i16test_export_without_position_quantization— Full precision positionstest_full_sh_buffer_size_larger— Buffer size verification for SH datatest_quantized_positions_smaller— Quantization size reductiontest_quantized_positions_bounds_accuracy— Quantization precisiontest_export_with_meshopt_compression— Meshopt compression (with feature flag)test_meshopt_compression_reduces_size— Compression ratio verificationtest_meshopt_buffers_have_correct_layout— Buffer layout validationtest_meshopt_compression_stats— Compression statisticstest_export_without_meshopt_no_extension— Without compression flagtest_color_quantization— Color quantization to u8test_empty_cloud— Empty cloud export handlingtest_large_cloud— Large dataset performancetest_compression_stats_structure— Stats metadata structure
Test Coverage Summary
| Module | Test Count | Coverage |
|-----------|--------|----------|
| lib.rs | 2 | Core data structures |
| token.rs | 2 | JWT validation |
| ply.rs | 3 | PLY parsing |
| clean.rs | 11 | Cleaning algorithms |
| gltf.rs | 24 | glTF/glb export |
| Total | 42 | Comprehensive |
Browser Support
| Browser | Version | Notes | |---------|---------|-------| | Chrome | 80+ | Full support | | Firefox | 75+ | Full support | | Safari | 14+ | Full support | | Edge | 80+ | Full support |
Requirements:
- WebAssembly (Wasm) support
- ES2020+ (BigInt, dynamic imports)
Project Structure
packages/sdk/
├── src/
│ ├── client.ts # Main Stereos class
│ ├── types.ts # TypeScript interfaces
│ └── index.ts # Public exports
├── wasm/ # WebAssembly files (generated)
│ ├── stereos_wasm.js
│ ├── stereos_wasm_bg.wasm
│ └── *.d.ts
├── dist/ # Compiled output (generated)
├── package.json
├── tsconfig.json
└── README.mdSupport
- Documentation: In Development
- Issues: https://github.com/StereosOrg/stereos/issues
- Email: [email protected]
