Skip to content

App Module Architecture

The App module implements a Manager-based Architecture where the App class serves as a slim orchestrator. Responsibilities are separated into dedicated managers that handle specific domains: data management, file loading, rendering, animation, and user interaction. This design improves modularity, testability, and scalability for scenes that mix static and ONNX-driven splats.

High-level layout

┌────────────────────────────────────────────────────────────┐
│ Presentation layer                                         │
│  UIController  ◄───►  DOMElements  ◄───►  Canvas/Events    │
└───────────────▲────────────────────────────────────────────┘
                │ callbacks
┌───────────────┴────────────────────────────────────────────┐
│ Application layer                                          │
│  App (orchestrator)                                        │
│  - initWebGPU_onnx()                                       │
│  - create GaussianRenderer                                 │
│  - wire managers & start RenderLoop                        │
└───────────────┬────────────────────────────────────────────┘
                │ owns
┌───────────────┴────────────────────────────────────────────┐
│ Manager layer                                              │
│  ModelManager      FileLoader      ONNXManager             │
│  CameraManager     AnimationManager RenderLoop             │
└───────────────┬────────────────────────────────────────────┘
                │ uses
┌───────────────┴────────────────────────────────────────────┐
│ Core subsystems                                            │
│  PointCloud / DynamicPointCloud  •  GaussianRenderer       │
│  Preprocess (SH/RGB)              •  GPURSSorter           │
└────────────────────────────────────────────────────────────┘

Key Design Principles

  • Separation of Concerns: Each manager handles a specific domain (models, files, camera, animation, rendering)
  • Facade Pattern: App class provides a simple public API while delegating to managers
  • Multiple Model Support: ModelManager supports concurrent models (PLY, SPZ, KSplat, SPLAT, SOG, ONNX, FBX)
  • Format Agnostic: Uses defaultLoader from IO module for unified format handling
  • ORT Integration: WebGPU device sharing with ONNX Runtime for efficient GPU usage
  • Render Loop Ownership: RenderLoop encapsulates the RAF lifecycle; App only launches and monitors it
  • Dynamic Content: ONNX integration runs GPU-only with shared devices—no CPU readbacks

Responsibility Quick Reference

Manager Responsibility Key Features
App Orchestration, dependency injection, UI callback surface Public API facade, initialization coordination
ModelManager Metadata storage, visibility filters, model tracking ModelEntry management, ID generation, capacity limits
FileLoader Unified asset ingestion Supports PLY, SPZ, KSplat, SPLAT, SOG, compressed PLY, FBX
ONNXManager ONNX model lifecycle Device sharing, session management, dynamic generators
CameraManager Camera and controller management Orbit/FPS switching, auto-framing, resize handling
AnimationManager Dynamic content updates Per-frame updates, throttling, performance tracking
RenderLoop Frame cycle coordination RAF loop, FPS counting, render pipeline, state management

Initialization Flow

  1. DOM Validation - Query and cache DOM elements via DOMElements
  2. Manager Creation - Instantiate all managers (Model, File, ONNX, Camera, Animation, RenderLoop)
  3. ORT Environment - Initialize ONNX Runtime environment with WASM paths
  4. WebGPU Context - Acquire shared WebGPU device with initWebGPU_onnx (with fallback)
  5. Renderer Setup - Instantiate GaussianRenderer and ensure sorter is ready
  6. Camera Initialization - Initialize perspective camera and default controller (FPS)
  7. UI Binding - Bind event listeners via UIController
  8. Resize Handler - Setup window resize handler
  9. RenderLoop Configuration - Initialize RenderLoop with GPU context and callbacks
  10. Start Loop - Begin the animation frame loop

Event & Data Flow

UI → UIController → App callbacks → Managers → Model state → RenderLoop → Renderer

Example Flows

File Loading (Gaussian formats):

File dropped → UIController.onFileLoad → App.loadFile → 
  App.loadGaussianModel → FileLoader.loadFile → 
  defaultLoader (IO module) → PointCloud creation → 
  ModelManager.addModel → RenderLoop renders

ONNX Model Loading:

App.loadONNXModel → ONNXManager.loadONNXModel → 
  ONNXGenerator creation → DynamicPointCloud creation → 
  ModelManager.addModel → AnimationManager tracks → 
  RenderLoop updates per-frame → Renderer renders

Camera Control:

UI button click → UIController.onControllerSwitch → 
  App.switchController → CameraManager.switchController → 
  Controller update → RenderLoop uses new camera matrix

Dynamic Animation:

App.controlDynamicAnimation → AnimationManager.controlDynamicAnimation → 
  RenderLoop.updateDynamicPointClouds → DynamicPointCloud.update → 
  ONNX inference → GPU buffer update → Renderer renders

WebGPU Context Sharing

initWebGPU_onnx() implements a two-stage initialization:

  1. ORT Integration Attempt: Tries to reuse the ONNX Runtime device (optionally forcing creation with a dummy model)
  2. Fallback: If ORT integration fails, falls back to standalone WebGPU initialization

Benefits

  • Single Device: Both ONNX inference and rendering use the same GPU device
  • Resource Efficiency: Shared device reduces memory overhead
  • Feature Consistency: Requested limits and features (shader-f16, timestamp queries) are applied once
  • Buffer Compatibility: No hidden devices, eliminating buffer compatibility issues
  • Graceful Degradation: Fallback ensures the app works even if ORT integration fails

Configuration Options

interface WebGPUInitOptions {
  preferShareWithOrt?: boolean;      // Prefer ORT device sharing
  dummyModelUrl?: string | null;      // Force ORT device creation
  adapterPowerPreference?: 'high-performance' | 'low-power';
  allowOwnDeviceWhenOrtPresent?: boolean;  // Allow separate device
}

Render Loop Ownership

RenderLoop owns the entire frame cycle:

  • Scheduling: requestAnimationFrame lifecycle management
  • Timing: Delta time calculation and FPS counting
  • State: Background color, Gaussian scale, render state
  • Coordination: Per-frame calls to managers and renderer

Frame Cycle

Each frame executes in order:

  1. Calculate Delta Time - dt = currentTime - lastTime
  2. Camera Update - CameraManager.update(dt)
  3. Dynamic Updates - AnimationManager.updateDynamicPointClouds(view, proj, time)
  4. FPS Counting - Update internal counters
  5. Rendering - GaussianRenderer.prepareMulti(...) / renderMulti(...)
  6. Callback Invocation - FPS and point count callbacks

App Public API

App exposes test-friendly helpers that delegate to managers:

Model Management: - getModels() - Get simplified model info - getModelWithPointCloud(type, id?) - Get full model entry - getFullModels() - Get all model entries

Loading: - loadGaussian(input, options?) - Load any Gaussian format - loadSPZ(input, options?) - Load SPZ format - loadKSplat(input, options?) - Load KSplat format - loadSplat(input, options?) - Load SPLAT format - loadSOG(input, options?) - Load SOG format - loadONNXModel(path, name?, staticInference?) - Load ONNX model

Camera: - resetCamera() - Reset to default position - switchController('orbit'|'fps') - Switch camera mode - getCameraMatrix() - Get current view matrix - getProjectionMatrix() - Get current projection matrix

Rendering: - setGaussianScale(scale) - Set splat size - getGaussianScale() - Get current scale - setBackgroundColor(color) - Set background RGBA - getBackgroundColor() - Get current background

Animation: - controlDynamicAnimation(action, speed?) - Control dynamic models - setDynamicAnimationTime(time) - Set animation time - getDynamicPerformanceStats() - Get performance metrics

Diagnostics & Debugging

Debug Information

App.getDebugInfo() aggregates comprehensive state from all managers:

{
  app: {
    initialized: boolean,
    canvas: { width, height },
    supportedFormats: { gaussian, onnx, models, all }
  },
  models: {
    count: number,
    capacity: number,
    totalPoints: number,
    visiblePoints: number
  },
  camera: CameraManager.getDebugInfo(),
  animation: AnimationManager.getDebugInfo(),
  renderLoop: RenderLoop.getDebugInfo(),
  onnx: {
    hasModels: boolean,
    modelCount: number,
    performanceStats: object
  }
}

Manager Access

For advanced usage, managers can be accessed directly:

  • app.getModelManager() - Direct ModelManager access
  • app.getONNXManager() - Direct ONNXManager access
  • app.getCameraManager() - Direct CameraManager access
  • app.getAnimationManager() - Direct AnimationManager access
  • app.getRenderLoop() - Direct RenderLoop access

Format Support

App.getSupportedFormats() returns all supported file extensions:

{
  gaussian: ['.ply', '.spz', '.ksplat', '.splat', '.sog', '.compressed.ply'],
  onnx: ['.onnx'],
  models: ['.gltf', '.glb', '.obj', '.fbx', '.stl'],
  all: [...all extensions]
}

Performance Monitoring

  • RenderLoop.getPerformanceInfo() - Real-time FPS and frame times
  • AnimationManager.getDynamicPerformanceStats() - ONNX inference metrics
  • ONNXManager.getONNXPerformanceStats() - Generator and point cloud counts

The manager architecture improves modularity, testability, and scalability for scenes that mix static and ONNX-driven splats.