SuperSplat File Handling and Drag-and-Drop: How the Import Pipeline Works

SuperSplat centralizes all file import operations in src/file-handler.ts, supporting native file pickers, fallback HTML inputs, and drag-and-drop through a unified importFiles pipeline that converts dropped items into a virtual file system before loading Gaussian splat models.

SuperSplat, the open-source 3D Gaussian Splatting editor from the PlayCanvas team (playcanvas/supersplat), provides multiple entry points for bringing assets into the scene. Whether users select files through the operating system dialog, drop them onto the canvas, or load entire animation sequences, all paths converge in a single orchestration layer that handles format detection, virtual file system creation, and scene integration.

The Central File Handler Architecture

The entry point for all file operations is the initFileHandler function exported from src/file-handler.ts. When the editor initializes (typically in src/editor.ts), the system invokes initFileHandler(scene, events, dropTarget) to wire up the three input mechanisms and register the public API functions scene.import, scene.openAnimation, scene.export, and scene.write.

At lines 62-64 of file-handler.ts, the handler registers itself with the events system:

events.function('import', async () => {
    return importFiles(await showOpenFilePicker(), false);
});

This registration allows UI components like the toolbar to trigger imports via events.invoke('scene.import').

Three Import Pathways

SuperSplat supports three distinct input methods to maximize browser compatibility and user convenience. Each method ultimately constructs an ImportFile[] array—containing filename, File object, and optional file-system handle—and passes it to the core importFiles function.

Native File Picker

Modern browsers supporting the File System Access API use window.showOpenFilePicker (lines 52-58). This opens the OS native file picker, reads selected file handles, and maps them directly to the ImportFile structure without creating temporary copies.

Fallback HTML Input

For browsers lacking the File System Access API, the system creates a hidden <input type="file"> element (lines 66-88). Configured to accept all supported extensions (.ply, .splat, .sog, .json, etc.), this input's onchange handler builds the same ImportFile[] structure, ensuring consistent behavior across legacy environments.

Drag-and-Drop Handler

The third path uses CreateDropHandler imported from src/drop-handler.ts. Attached to the supplied dropTarget element (usually the main canvas), this handler captures dragenter, dragover, dragleave, and drop events. When files are dropped, the handler extracts file-system entries and converts them into ImportFile objects (lines 90-99):

CreateDropHandler(dropTarget, (entries, shift) => {
    const files = entries.map(e => ({
        filename: e.filename,
        contents: e.file,
        handle: e.handle
    }));
    importFiles(files);
});

The Import Pipeline

Once importFiles receives the file array, it executes a four-stage pipeline to prepare assets for the WebGL scene.

Format Detection

Before loading, helper functions isPlySequence, isSog, and isLcc (lines 13-45) examine lower-cased filenames to categorize the payload. The system distinguishes between:

  • PLY animation sequences (multiple sequential .ply files)
  • SOG scene bundles
  • LCC asset bundles
  • Individual model or project files

Virtual File System Creation

To resolve relative asset paths without copying files to disk, the system instantiates MappedReadFileSystem from src/io/browser-file-system.ts (line 85). If the main file originated from a remote URL, this virtual file system uses the base URL to resolve dependencies. All supplied File objects are added via fileSystem.addFile, creating a read-only sandbox for the loader.

Model Loading and Scene Integration

The actual asset loading occurs at line 95:

const model = await scene.assetLoader.load(filename, fileSystem, animationFrame);
scene.add(model);

The assetLoader reads the primary file (PLY, SPLAT, SOG, etc.) using the virtual file system, parses the Gaussian-splat data into a Splat instance, and inserts it into the active Scene managed by src/scene.ts and src/scene-state.ts.

Special Format Handling

PLY Sequences

When the detector identifies a PLY sequence, the handler fires two UI events (lines 110-113):

events.fire('plysequence.setFrames', files.map(f => f.contents));
events.fire('timeline.frame', 0);

This populates the timeline with animation frames and jumps to the first frame.

SOG and LCC Bundles

For SOG and LCC formats, the system prompts for user confirmation (lines 115-124) before proceeding through the same importSplatModel path. This prevents accidental overwrites of complex scene state.

Individual File Routing

For non-sequence imports, the loop starting at line 138 iterates over each entry and routes files to specialized loaders based on extension:

  • .ssproj (project files)
  • .ply, .splat (Gaussian splats)
  • .sog (scene bundles)
  • images.txt, .json (configuration data)

Drag-and-Drop Implementation Details

The CreateDropHandler in src/drop-handler.ts serves as a thin DOM abstraction layer. It transforms the native HTML5 drag-and-drop API into a callback-based interface that yields file-system entries. This design keeps browser-specific event handling separate from the import logic, ensuring that file-handler.ts treats dropped files identically to those selected via the file picker.

The handler manages CSS drag states and extracts FileSystemEntry objects from the DataTransferItemList, converting them to the standard ImportFile shape before invoking the callback passed from initFileHandler.

Summary

  • Centralized import logic lives in src/file-handler.ts, specifically the initFileHandler and importFiles functions.
  • Three entry points—native file picker (lines 52-58), fallback HTML input (lines 66-88), and drag-and-drop (lines 90-99)—all converge on the same ImportFile[] structure.
  • Format detection uses isPlySequence, isSog, and isLcc (lines 13-45) to determine loading strategy.
  • Virtual file system (MappedReadFileSystem at line 85) enables the loader to resolve relative paths in drag-and-drop scenarios.
  • Drag-and-drop relies on CreateDropHandler from src/drop-handler.ts to normalize DOM events into file entries.
  • Scene integration occurs via scene.assetLoader.load followed by scene.add, with special event firing for PLY sequences (plysequence.setFrames).

Frequently Asked Questions

How does SuperSplat handle file imports in browsers without the File System Access API?

SuperSplat creates a hidden <input type="file"> element configured with all supported extensions. When triggered, this fallback input's onchange handler constructs the same ImportFile[] array used by the native picker, ensuring consistent behavior across legacy browsers like Safari or older Firefox versions.

What happens when I drag and drop a folder containing PLY files?

The CreateDropHandler extracts all file entries from the dropped folder and passes them to importFiles. If the filenames match a sequential pattern, isPlySequence identifies them as an animation sequence, firing plysequence.setFrames to load them into the timeline and automatically setting the current frame to zero.

Where does SuperSplat store files during the import process?

SuperSplat does not copy files to persistent storage. Instead, it creates a MappedReadFileSystem instance in memory (implemented in src/io/browser-file-system.ts) that maps the original File objects into a virtual directory structure. This allows the asset loader to resolve relative paths (like textures referenced by JSON) while keeping the data in browser memory.

Can I programmatically trigger a file import without user interaction?

Yes. The initFileHandler registers an event function at lines 62-64 that exposes events.invoke('scene.import'). Your code can call this method to trigger the file picker programmatically, or you can directly call importFiles with a pre-constructed array of ImportFile objects if you already have access to the file data.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →