Skip to content

Repository files navigation

Splattr

A free, open-source desktop studio for Gaussian Splatting. Take a folder of photos or a video all the way to a trained splat, through three steps: Assets → Pointcloud → Splat.

Splattr is a clean UI layer over two established open-source tools: COLMAP for structure-from-motion / sparse reconstruction, and OpenSplat for training the Gaussian splat. Think of it as a lightweight, open alternative to tools like PostShot.

Status

Early scaffold. The app shell, project format, and 3D viewport are real and working. The Assets step is fully real: drag-and-drop or native-picker import, splitting videos into frames via a bundled ffmpeg binary with live progress, a live thumbnail gallery (grid or list view) in place of the 3D viewport while on that tab, and per-asset delete with an optional per-project "don't ask again" preference. COLMAP and OpenSplat are not yet wired to real subprocesses: the Pointcloud/Splat steps currently simulate a run (progress bar, live viewport population with placeholder data) so the full workflow and UI can be exercised end to end. Wiring real colmap / opensplat binary execution is the next milestone; see Roadmap.

Tech stack

  • Electron + electron-vite: desktop shell, fast HMR for main/preload/renderer
  • React 18 + TypeScript
  • Tailwind CSS v4: theme tokens defined in src/renderer/src/styles/main.css
  • Three.js + React Three Fiber + drei: the live 3D viewport
  • Zustand: renderer state (project-store, scene-store)
  • chokidar: watches a project's assets/, colmap/, and splat/ folders in the main process and pushes change events to the renderer, so the asset gallery and 3D viewport both update live as files change on disk
  • ffmpeg-static: bundled ffmpeg binary used to split imported videos into frames (no system ffmpeg install required)
  • lucide-react: icon set
  • IBM Plex Sans / IBM Plex Mono (self-hosted via @fontsource): typography

Getting started

pnpm install
pnpm dev

This launches the Electron app with hot-reload on the renderer and auto-restart on main/preload changes.

Gotcha: if your shell has ELECTRON_RUN_AS_NODE=1 set (some Node/Electron tooling sets this globally), Electron launches as plain Node and crashes with Cannot read properties of undefined (reading 'isPackaged'). If you hit that, run:

env -u ELECTRON_RUN_AS_NODE pnpm dev

Other scripts:

pnpm typecheck      # tsc --noEmit for both the node (main/preload) and web (renderer) projects
pnpm build           # production build (out/)
pnpm build:mac       # build + electron-builder mac target
pnpm build:win       # build + electron-builder win target
pnpm build:linux     # build + electron-builder linux target

The .splattr project format

A project is a folder (e.g. My Scan.splattr) containing:

My Scan.splattr/
├── project.json   # manifest: name, timestamps, per-step state (see src/shared/types.ts)
├── assets/        # imported source images / video
├── colmap/        # COLMAP working directory (database, sparse/dense reconstruction)
├── splat/         # OpenSplat output (trained .ply and checkpoints)
└── logs/

project.json tracks per-step status (empty | ready | running | complete | error), tool-specific settings (camera model, matcher type, target iterations, etc.), and per-project preferences (e.g. whether to skip the "delete asset" confirmation). See the ProjectManifest type in src/shared/types.ts for the exact shape.

Project structure

src/
├── main/                 # Electron main process
│   ├── index.ts          # window creation, IPC handler registration
│   ├── project.ts        # .splattr create/open/read/save + recent-projects list
│   ├── assets.ts         # file picker, import, list/delete assets, video frame extraction
│   ├── ffmpeg.ts          # spawns the bundled ffmpeg-static binary, parses progress
│   └── watcher.ts        # chokidar wrapper, pushes fs change events over IPC
├── preload/
│   ├── index.ts          # contextBridge: exposes `window.splattr` to the renderer
│   └── index.d.ts         # ambient Window typing for the renderer
├── shared/                # imported by main, preload, AND renderer: the single
│   │                      # source of truth for the IPC contract
│   ├── types.ts           # ProjectManifest, step states, .splattr layout constants
│   ├── ipc-channels.ts    # IPC channel name constants
│   └── preload-api.ts     # the `SplattrAPI` interface implemented by preload/index.ts
└── renderer/src/
    ├── App.tsx            # ProjectGate vs AppShell, based on whether a project is open
    ├── components/
    │   ├── ui/            # generic, reusable primitives: Button, Modal, Tooltip,
    │   │                  # IconButton, Select, Checkbox, ProgressBar, StatRow, Logo
    │   ├── dialogs/       # Modal-based popups: NewProjectDialog, ImportAssetsDialog,
    │   │                  # ConfirmDialog
    │   ├── gate/          # pre-project screen (ProjectGate)
    │   ├── shell/         # app chrome: AppShell, Header, StepNav, StatusBar
    │   ├── panels/        # left Scene panel + right per-step Parameters panels
    │   │                  # (AssetsPanel, PointcloudPanel, SplatPanel)
    │   └── viewport/      # center content: the 3D viewport (Viewport + layers) for
    │                      # Pointcloud/Splat, the asset gallery (AssetGallery, AssetTile)
    │                      # for Assets — AppShell swaps between them by active step
    ├── store/             # zustand: project-store (manifest/steps), scene-store (3D data)
    ├── hooks/             # useProjectWatcher, wires the main-process file watcher in
    └── lib/               # cn (tailwind-merge), format, path helpers

Component conventions

  • components/ui/: dumb, reusable, no app state. If you need a new button/badge/input variant, it goes here.
  • components/dialogs/: anything shown via <Modal>. Build new popups on top of Modal rather than rolling a new overlay.
  • components/panels/ and components/shell/: app-specific composition, free to depend on the zustand stores.
  • Prefer a Tooltip over an inline caption/help paragraph for anything that's only relevant on hover (disabled-button reasons, icon-only button labels, secondary hints). Reserve visible body text for things the user needs to read without hovering.

Roadmap

  • Spawn real colmap (feature extraction, matching, mapper) as a child process, parse progress and the resulting sparse reconstruction (cameras.bin / images.bin / points3D.bin) into the viewport instead of the current placeholder point cloud
  • Spawn real opensplat, parse training progress and load the actual .ply output into the splat viewer
  • Video to frame extraction (bundled ffmpeg-static, configurable fps, live progress)
  • Poster-frame thumbnails for video assets that weren't split into frames
  • Persist Scene panel layer visibility per project
  • Export/share flow for a finished splat

License

MIT

About

No description or website provided.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages