Client SDK for Stereos - Convert Gaussian splats to glTF
npm install @stereos/sdk

> Client SDK for Stereos β Convert 3D Gaussian splats to optimized glTF/glb format entirely in the browser using WebAssembly.
- π 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
``bash`
npm install @stereos/sdk
`typescript
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);
`
``
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Your Application β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β βββββββββββββββ βββββββββββββββ βββββββββββββββββββ β
β β Stereos βββββΆβ WASM Core βββββΆβ glTF/glb Output β β
β β Client β β (stereos-wasm)β β β β
β βββββββββββββββ βββββββββββββββ βββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββ β
β β Token API β (Secure short-lived JWT authentication) β
β βββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
#### 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
`typescript`
class Stereos {
private apiKey: string;
private wasm: WasmModule | null;
private token: string | null;
async convert(file, options): Promise
async validateToken(): Promise
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
#### Constructor
`typescript`
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.
`typescript`
async convert(
file: File | Blob | ArrayBuffer | Uint8Array,
options?: ConvertOptions
): Promise
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.
`typescript`
async validateToken(): Promise
Returns:
`typescript`
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.
`typescript`
async version(): Promise
##### Stereos.download(result) (static)
Trigger a browser download of the conversion result.
`typescript`
static download(result: ConvertResult): void
##### Stereos.createDownloadUrl(result) (static)
Create a blob URL for the conversion result.
`typescript`
static createDownloadUrl(result: ConvertResult): string
#### StereosOptions
`typescript`
interface StereosOptions {
/* Your API key /
apiKey: string;
}
#### ConvertResult
`typescript`
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
`typescript`
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
`typescript`
interface ApiError {
error: string; // Error code
message?: string; // Human-readable message
}
`typescript
import { Stereos } from '@stereos/sdk';
const stereos = new Stereos({ apiKey: 'sk_live_...' });
const result = await stereos.convert(plyFile);
Stereos.download(result);
`
`typescript`
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);
});
Best for web delivery β smallest file size:
`typescript`
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
});
For view-dependent rendering with full spherical harmonics:
`typescript`
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,
},
});
`typescript
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);Removed ${removed_outliers} outliers
console.log();`
}
`typescriptConversions remaining: ${claims.conversions_remaining}
const claims = await stereos.validateToken();
console.log();Max file size: ${claims.max_file_size} bytes
console.log();`
`typescript`
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);
}
}
All tests are currently passing β
Run tests with: cargo test -p stereos-core
#### 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
| 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 | 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)
```
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.md
- Documentation: In Development
- Issues: https://github.com/StereosOrg/stereos/issues
- Email: james@atelierlogos.studio