MCPcopy Index your code
hub / github.com/buildoak/tortuise

github.com/buildoak/tortuise @v0.1.1

Chat with this repo
repository ↗ · DeepWiki ↗ · release v0.1.1 ↗ · + Follow
172 symbols 401 edges 36 files 3 documented · 2%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

tortuise

Gaussian splats viewer that works in your terminal. Yes, it's made of symbols!

tortuise demo

License: MIT Platforms

A CPU-first 3D Gaussian Splatting viewer inspired by ratatui, built on crossterm. Fully parallelized rendering pipeline via rayon, perceptual color mapping, six render modes — all running on pure CPU. Real scenes with 1.1M splats hold 10–25 FPS. No GPU required. Works on potato.

Why this exists

Gaussian Splats are cool. Beautiful tech. Especially now with the rise of image-to-splat pipelines like SHARP. Btw, it will be soon available here.

Inspiration by ratatui merged with binge watching of Common Side Effects which resulted in building this "The Tortuise" TUI 3GS viewer.

Features

Feature Details
6 render modes Halfblock (default), point cloud, matrix, block density, braille, ASCII. Cycle with M
Full 3D navigation WASD movement, R/F vertical, arrow keys for yaw/pitch. Smooth held-key input
Two camera modes Free (fly anywhere) and Orbit (auto-rotate around origin). Switch with Space
.ply and .splat files Standard 3DGS formats. Binary little-endian PLY with SH coefficients, 32-byte .splat records
Built-in scenes Bundled bonsai.splat (1.1M splats) and luigi.ply (14K). --demo generates a procedural torus knot — no files needed
Terminal detection Truecolor for modern terminals, perceptual 256-color fallback for Terminal.app. Zero config
Supersampling 1x/2x/3x factor for higher fidelity at the cost of compute
Cross-platform macOS and Linux

Quick start

Requires Rust 1.80+ (rustup update to upgrade)

# From source (recommended for now)
git clone https://github.com/buildoak/tortuise.git
cd tortuise
cargo install --path .

# Built-in demo (no scene file needed)
tortuise --demo

# Load a bundled scene
tortuise --flip-y scenes/bonsai.splat

# Load any .ply or .splat file
tortuise your-scene.splat
tortuise your-scene.ply

# Some scenes need axis flips depending on capture coordinate system
tortuise --flip-y scene.ply
tortuise --flip-z scene.splat

CLI options

tortuise [OPTIONS] [INPUT]

Arguments:
  [INPUT]    Path to a .ply or .splat scene file (runs demo if omitted)

Options:
  --demo              Run built-in demo scene
  --flip-y            Flip Y axis (some capture tools use Y-down)
  --flip-z            Flip Z axis
  --supersample <N>   Supersampling factor [default: 1]
  --cpu               Force CPU rendering
  -h, --help          Print help
  -V, --version       Print version

Controls

Free mode

Key Action
W / A / S / D Move forward / left / back / right
R / F Move up / down
Arrow keys Yaw and pitch (look around)
Space Switch to Orbit mode
M Cycle render mode
+ / - Adjust movement speed
Tab Toggle HUD
Z Reset camera
Q / Esc Quit

Orbit mode

Key Action
Arrow Up / Down Adjust elevation
Arrow Left / Right Nudge orbit angle
Space Switch to Free mode
+ / - Adjust orbit speed

Supported terminals

Truecolor (best experience): Ghostty, iTerm2, Kitty, WezTerm, Alacritty

256-color fallback: Apple Terminal.app -- works, but reduced color fidelity. The perceptual color mapping does its best.

Auto-detected via COLORTERM, TERM_PROGRAM, and TERM environment variables. No configuration needed.

Tested hardware

Device CPU Scene Reference FPS
Mac Mini M4 Apple M4 luigi.ply (14K) 120+
Mac Mini M4 Apple M4 bonsai.splat (1.1M) 80+
MacBook Air M2 Apple M2 luigi.ply (14K)
MacBook Air M2 Apple M2 bonsai.splat (1.1M) 20–30
Jetson Orin Nano* ARM Cortex-A78AE luigi.ply (14K) ~30
Jetson Orin Nano* ARM Cortex-A78AE bonsai.splat (1.1M) 10–15

Numbers in parentheses are splat counts. FPS is approximate and depends heavily on terminal window size — a smaller window (⌘-) renders fewer cells and runs faster. *Jetson tested over SSH, which may be a bottleneck.

Where to get scenes

  • tortuise --demo for an instant procedural scene -- no downloads required
  • SuperSplat -- open-source editor and converter for Gaussian splat scenes
  • Polycam -- photogrammetry captures, some with Gaussian splat export
  • nerfstudio -- train your own splats from video, exports to .ply
  • Any standard 3DGS pipeline output in .ply or .splat format

Both formats are well-supported: PLY files with spherical harmonic coefficients (f_dc_0/1/2) or direct RGB, and the compact 32-byte .splat format used by most web viewers.

How it works

The pipeline is straightforward: load splats, project them into screen space, depth-sort, splat onto a framebuffer, then convert to terminal characters. Each frame:

  1. Project -- every Gaussian is transformed from world space through the camera view matrix. Frustum culling drops anything behind the near plane or outside the viewport. This step is parallelized with rayon.
  2. Sort -- projected splats are depth-sorted back-to-front for correct alpha compositing.
  3. Rasterize -- each splat is splatted onto an RGB framebuffer using its 2D covariance (scale + rotation). Front-to-back compositing with early alpha termination -- once a pixel is fully opaque, all remaining splats behind it are skipped. Per-splat saturation probes skip entire Gaussians when they land on already-saturated regions. At 1M+ splats, the back 80% are often invisible behind the front 20%.
  4. Encode -- the framebuffer is converted to terminal output. In halfblock mode, each cell packs two vertical pixels using the character with separate foreground/background colors. Other modes use braille patterns, ASCII density ramps, or single characters.

The frame target is 8ms (~120fps). On truecolor terminals, colors are passed as 24-bit RGB. On 256-color terminals, a perceptual distance function maps each pixel to the closest ANSI color -- weighted toward green sensitivity, which is where human vision is sharpest.

Roadmap

Things I want to improve next -- and contribution opportunities:

  • Kitty graphics protocol -- pixel-perfect rendering via the terminal image protocol. Roughly 18x the resolution of half-block characters. This is the big one.
  • SHARP integration -- image-to-splat-to-view pipeline. Single photo to 3D in your terminal.
  • Sample scene bundle -- curated downloadable scenes so people can skip the "where do I find a .splat file" step.
  • GPU acceleration -- a Metal compute backend exists behind a feature flag, but needs work at higher resolutions. Parked, not abandoned.
  • Performance -- radix sort for depth ordering, SIMD-accelerated projection via glam, tighter memory layout.

Built with

  • Rust
  • crossterm -- terminal control and input
  • rayon -- data parallelism for projection and rasterization
  • clap -- CLI argument parsing

ratatui + tortoise = tortuise.

License

MIT -- Nick Oak, 2026

Core symbols most depended-on inside this repo

clamp_u8
called by 29
src/math.rs
new_private_buffer
called by 19
src/render/metal/pipeline.rs
create_pipeline
called by 8
src/render/metal/pipeline.rs
new_shared_buffer
called by 8
src/render/metal/pipeline.rs
div_ceil_u32
called by 8
src/render/metal/sort.rs
bytes_for_u32_elems
called by 7
src/render/metal/buffers.rs
render
called by 6
src/render/metal/render.rs
framebuffer_slice
called by 6
src/render/metal/buffers.rs

Shape

Function 108
Method 42
Class 16
Enum 6

Languages

Rust100%

Modules by API surface

src/math.rs19 symbols
src/render/metal/tests.rs15 symbols
src/camera.rs13 symbols
src/render/metal/buffers.rs12 symbols
src/render/mod.rs10 symbols
src/render/metal/pipeline.rs10 symbols
src/input/mod.rs9 symbols
src/splat.rs7 symbols
src/render/frame_halfblock.rs7 symbols
src/parser/ply.rs7 symbols
src/render/metal/sort.rs5 symbols
src/input/state.rs5 symbols

For agents

$ claude mcp add tortuise \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact