README.md

August 19, 2026 · View on GitHub

StrucTTY Logo

StrucTTY

Interactive, Terminal-Native Protein Structure Viewer

C++17 Platform License Build bioRxiv


StrucTTY is a lightweight, terminal-based protein structure visualizer built in C++17. It renders 3D protein structures directly in the terminal using Unicode Braille sub-pixel rendering, providing 8x resolution compared to standard character-based rendering.

StrucTTY supports simultaneous visualization of up to 9 proteins, 9 color modes with 3-band depth fog, and integrates with Foldseek and FoldMason for structural search and multiple structure alignment.

Features

  • Braille sub-pixel rendering — each terminal cell maps to a 2×4 logical pixel grid
  • Up to 9 proteins rendered simultaneously with independent controls
  • 9 color modesprotein, chain, rainbow, plddt, interface, conservation, align, align-fs, align-near
  • 3-band depth fog — near (bright), mid (normal), far (dark with hue retention) for depth perception
  • Secondary structure visualization — helix cylinders and sheet ribbons
  • Foldseek integration — load .m8/_report results (-fsr), navigate hits, and take targets from a Foldseek DB, a local directory, or automatic download (-fst)
  • FoldMason integration — MSA superposition with conservation coloring
  • MSA conservation scoring — Shannon entropy from FASTA/A3M alignments
  • Interface detection — inter-chain contact residue highlighting
  • Alignment visualization — structural alignment region highlighting
  • Screenshot export — PNG output via stb_image_write
  • Chain selection — filter specific chains per protein

Installation

Requirements

DependencyVersion
C++ compilerGCC ≥ 7.1 or Clang ≥ 5.0 (C++17)
CMake≥ 3.15

Supported platforms: Linux, macOS

Build (Linux / macOS)

git clone --recurse-submodules https://github.com/steineggerlab/StrucTTY.git
cd StrucTTY
mkdir build && cd build
cmake ../ -DCMAKE_BUILD_TYPE=Release
make -j $(nproc)

The output binary will be generated at build/StrucTTY.

Example data

The structures used below are distributed separately. Unpack them in the repository root, next to build/:

cd StrucTTY
curl -L -o example.zip https://github.com/user-attachments/files/31204019/structty_example.zip
unzip -d example example.zip && rm example.zip

Quick Start

Single structure

./StrucTTY ../example/1NPL-assembly1.cif --mode chain

https://github.com/user-attachments/assets/64c37f49-7ae8-4dea-bec3-37575583a767

Secondary structure visualization

./StrucTTY ../example/3HGM-assembly1.cif --mode chain
./StrucTTY ../example/3HGM-assembly1.cif --mode chain -s

https://github.com/user-attachments/assets/01d91bc2-4c49-46f8-80c5-d736ccdeea07

Color modes

./StrucTTY ../example/3A0C-assembly1.cif                  # protein (default)
./StrucTTY ../example/3A0C-assembly1.cif --mode chain
./StrucTTY ../example/3A0C-assembly1.cif --mode rainbow
./StrucTTY  ../example/3A0C-assembly1_colabfold.pdb --mode plddt
./StrucTTY ../example/3A0C-assembly1.cif --mode interface
./StrucTTY ../example/3A0C-assembly1.cif --mode conservation \
  --msa ../example/msa_result/query.a3m

https://github.com/user-attachments/assets/d4fea46a-2dc5-4e92-85cc-aa47ebcdb2d1

Multiple structures

./StrucTTY ../example/1CJK-assembly1.cif \
  ../example/1NPL-assembly1.cif \
  ../example/3A0C-assembly1.cif \
  ../example/3HGM-assembly1.cif \
  ../example/3OAG-assembly1.cif \
  ../example/9FL9-assembly1.cif \
  ../example/AF-A0A233SAX3-F1-model_v6.cif \
  ../example/9N47-assembly1.cif \
  ../example/8KGM-assembly1.cif

https://github.com/user-attachments/assets/eb2fae4c-4f64-489e-b891-26505d55179c

Chain selection

./StrucTTY ../example/9N47-assembly1.cif -m chain
./StrucTTY ../example/9N47-assembly1.cif -c ../example/chainfile_9N47.tsv -m chain

https://github.com/user-attachments/assets/f9cfca51-bba3-4090-b021-a93ad1e671bc

Foldseek hit navigation

The same hit under both alignment modes — align-fs colours only what Foldseek aligned, align-near colours whatever ends up close in space:

./StrucTTY ../example/foldseek_result/DB1/3cna-assembly1.cif \
  -fst ../example/foldseek_result/DB2/ \
  -fsr ../example/foldseek_result/result \
  -m align-fs -s
./StrucTTY ../example/foldseek_result/DB1/3cna-assembly1.cif \
  -fst ../example/foldseek_result/DB2/ \
  -fsr ../example/foldseek_result/result \
  -m align-near -s

In the recording, the right pane is align-fs and the left one is align-near.

https://github.com/user-attachments/assets/0118ba8f-e77e-455c-87c8-197f008ffe7a

Pass the query directory instead of one structure to walk every query in the result with ]/[:

./StrucTTY ../example/foldseek_result/DB1/ \
  -fst ../example/foldseek_result/DB2/ \
  -fsr ../example/foldseek_result/result \
  -m align-fs -s

FoldMason MSA superposition

./StrucTTY ../example/3A0C-assembly1.cif  ../example/L7RCY6.pdb 
./StrucTTY ../example/3A0C-assembly1.cif  ../example/L7RCY6.pdb \
  -fm ../example/foldmason_result/foldmason.json -m align

FoldMason_alignment

Usage

./StrucTTY <query...> [OPTIONS]
OptionDescription
-m, --mode <MODE>Color mode: protein (default), chain, rainbow, plddt, interface, conservation, align, align-fs, align-near
-c, --chains <FILE>Chain selection file (TSV: index + chain IDs)
-s, --structureShow secondary structure (helix/sheet)
--msa <FILE>MSA file for conservation scoring (FASTA/A3M)
-fst, --foldseek-target <PATH>Target source for Foldseek hits: Foldseek DB, structure directory, structure file, or auto (download from public DBs)
-fsr, --foldseek-result <FILE>Foldseek result: .m8 (12/17/21/29 columns) or multimer _report (14 columns)
-fm, --foldmason <FILE>FoldMason result (JSON or FASTA MSA)
-n, --nopanelHide info panel

-fst and -fsr must be given together — one without the other is an error.

Supported inputs

The kind of every input is detected automatically (no format flags):

KindWhat it isAccepted as
Structure file.pdb / .cif / .ent (+ .gz)query, -fst
Structure directorya directory of those filesquery, -fst
Foldseek DBbase path of a DB built from structures (needs <db>_ca)query, -fst
Foldseek result.m8 (12/17/21/29 columns) or multimer _report (14 columns)-fsr

A _report (14 columns) enters the multimer path. The query is where the per-complex chains are read from, so it must be a Foldseek query DB or a directory of structures.

With -fst <directory>, hits are looked up by accession. Foldseek splits multimers per chain (1dci-assembly1_B-2), so the trailing _<chain> is stripped until a file matches (1dci-assembly1.cif) and that chain alone is drawn — a plain directory of structures works as a target, no createdb needed.

Sequence FASTA is not supported. It carries no 3D coordinates, and foldseek createdb --prostt5-model predicts 3Di (_ss) without writing any _ca, so a sequence-derived DB cannot be rendered either. Both cases fail before rendering starts, with the reason printed.

Keyboard Controls

KeyAction
0Control all proteins
19Control individual protein
W / A / S / DMove up / left / down / right
X / Y / ZRotate around X / Y / Z axis
R / FZoom in / out
N / PNext hit / Previous hit
[ / ]Next query / Previous query
QQuit

Mouse hover displays residue information in the info panel.

Color Modes

ModeDescription
proteinOne color per protein (9 distinct colors, cycling)
chainOne color per chain (15 colors)
rainbowN→C gradient (20-step hue spectrum)
plddtAlphaFold confidence: blue (≥90), cyan (70–90), yellow (50–70), orange (<50)
interfaceInter-chain contacts (CA–CA < 8 Å): magenta vs. dim
conservationMSA Shannon entropy: blue (variable) → red (conserved)
alignStructurally aligned regions: bright vs. dim gray. Uses the alignment when the result has one, otherwise falls back to distance
align-fsOnly what Foldseek aligned (qaln/taln columns). Never falls back — errors out if the result carries no alignment
align-nearDistance only: residues with a counterpart within the cutoff, whatever the result says

The panel's Align: line names the colour source actually used: aln-string (Foldseek alignment), msa-col (FoldMason MSA columns) or nearest-nbr (distance). The Superpose: line in the hit block is a different thing — how the structures were laid on top of each other (aln-string, kabsch-qaln, ...) — and does not change with the colour mode.

The alignment-based modes need qaln/taln, i.e. a 17/21/29-column .m8. Foldseek's default 12-column output has none. Generate a usable result with --format-output query,target,fident,alnlen,mismatch,gapopen,qstart,qend,tstart,tend,evalue,bits,lddt,qtmscore,ttmscore,qaln,taln (the search itself must run with -a). foldseek ... --view-structty does this for you. Multimer _report files carry no alignment strings at all, so they only work with align-near.

All modes support 3-band depth fog: near (vivid), mid (normal), far (dark, hue-retaining).

Integrations

Foldseek

StrucTTY reads Foldseek easy-search output (.m8 format) with support for 12, 17, 21, and 29 column formats. Features include:

  • Interactive hit navigation with automatic structure downloading
  • Direct Foldseek DB reading (-fst <DB>) read Cα coordinates directly from Foldseek _ca database, eliminating network dependency. Uses hit-based selective scanning for minimal memory usage (~152KB for 1000 hits, even on AFDB50)
  • Structural superposition using U/T rotation-translation matrices
  • Alignment string visualization (qaln/taln)
  • Multi-database support: PDB, AlphaFold DB, ESMAtlas, CATH, BFVD, and more
  • Multi-query navigation (]/[) across queries in a single .m8. The query can be a Foldseek DB or a plain directory — each accession in the result is looked up inside it, so a standalone run reproduces what foldseek --view-structty shows
  • With a structure file as the query, hits are filtered to that file — one .m8 covering a whole query directory no longer walks you through other queries' hits. Foldseek indexes a multimer per chain, so ]/[ steps through the query's chains and N/P through that chain's hits; the panel shows Q[3/6][2 / 14]
  • Multimer (complex-level) report viewing with per-complex superposition

Launch from Foldseek

StrucTTY is embedded directly into Foldseek as a static library (add_subdirectory(lib/structty)), so no external binary or PATH lookup is required — Foldseek calls structty::run() in-process. The viewer opens automatically once the search finishes, reading the query and target structures directly from the search's temporary databases (folder/tar/gz inputs supported). Temporary DBs are kept alive for the viewer and cleaned up after it closes. Supported workflows: easy-search and easy-multimersearch.

Automatic launch after a search — add the --view-structty flag (it takes no value):

foldseek easy-search query.cif targetDir result.m8 tmp --view-structty \
  -a --format-output "query,target,fident,alnlen,mismatch,gapopen,qstart,qend,tstart,tend,evalue,bits,lddt,qtmscore,ttmscore,qaln,taln"

The viewer builds that 17-column layout internally either way; passing -a --format-output keeps a copy in result.m8, so the same hits can be reopened later with -fsr and still show lDDT, TM scores and the aligned regions. Without it the file keeps Foldseek's 12-column default, which carries no alignment — align-fs then refuses to run and align falls back to distance.

Multimer (complex-level) search — the viewer works out of the box, since the per-complex report (--multimer-report-mode 1) is the default; setting --multimer-report-mode 0 skips the launch. The viewer reads the _report file the workflow already writes, so it stays after the viewer closes:

foldseek easy-multimersearch queryDir targetDir result tmp --view-structty

This writes result_report, the 14-column file the viewer reads. Both sides must be complexes — a single-chain query or target yields an empty report, and the viewer then refuses it.

Standalone usage

First produce a result that carries the alignment. The two columns that matter are qaln and taln; -a makes the search keep the backtraces they come from:

foldseek easy-search query.pdb targetDir result.m8 tmp \
  -a --format-output "query,target,fident,alnlen,mismatch,gapopen,qstart,qend,tstart,tend,evalue,bits,lddt,qtmscore,ttmscore,qaln,taln"

Add --alignment-type 1 to align with TM-align instead of 3Di+AA — slower, but it usually reports a tighter set of aligned residues.

For a result that already exists as a Foldseek database, convert it instead of searching again:

foldseek convertalis queryDB targetDB resultDB result.m8 \
  --format-output "query,target,fident,alnlen,mismatch,gapopen,qstart,qend,tstart,tend,evalue,bits,lddt,qtmscore,ttmscore,qaln,taln"
# Read target Cα coordinates straight from a Foldseek DB (offline)
./StrucTTY query.pdb -fst /path/to/targetDB -fsr result.m8

# Look targets up by hit accession in a local structure directory
./StrucTTY query.pdb -fst /path/to/pdbs/ -fsr result.m8

# Download hit structures from public DBs (PDB, AFDB, ESMAtlas, CATH, BFVD, ...)
./StrucTTY query.pdb -fst auto -fsr result.m8

# Query from a Foldseek DB — multi-query navigation with ]/[
./StrucTTY /path/to/queryDB -fst /path/to/targetDB -fsr result.m8 -m align-fs

# Colour by distance instead, whatever the result says (works on 12-column files)
./StrucTTY query.pdb -fst /path/to/targetDB -fsr result.m8 -m align-near

# Multimer: 14-column _report, chains read per complex from the query DB or directory.
# A _report carries no alignment strings, so align-near is the only align mode.
./StrucTTY ../example/foldseek_result/DB1/ \
  -fst ../example/foldseek_result/DB2/ \
  -fsr ../example/foldseek_result/result_multimer_report -m align-near

FoldMason

StrucTTY loads FoldMason MSA results (JSON with Cα coordinates or FASTA) for:

  • Kabsch-based structural superposition
  • Column-wise conservation scoring
  • Gap-aware alignment visualization

MSA Conservation

Load FASTA or A3M multiple sequence alignments to compute per-residue conservation scores via Shannon entropy, visualized with the conservation color mode.

Performance

StrucTTY renders interactively even for large complexes. The table below measures load time, time-to-first-frame (TTFF), per-frame render time, and input-to-frame latency across structures of increasing size:

StructureLoad (ms)TTFF (ms)Frame mean (ms)Frame p95 (ms)Latency mean (ms)Latency p95 (ms)
1CRN465.444.6<0.5<0.5<0.5<0.5
1STP12112.4106.0<0.5<0.5<0.5<0.5
3BIK44647.0205.80.01<0.50.01<0.5
6VXX2916255.0894.42.973.002.993.00
4V4Q114633336.86199.213.3614.0013.6814.80
  • Frame time stays under the 16 ms (60 fps) budget even at 11,463 Cα — interaction remains smooth for structures spanning three orders of magnitude in size.
  • Input latency tracks frame time closely, so rotation/zoom feels responsive with no perceptible input lag.
  • Load and TTFF scale with atom count; the immediate z-test rasterizer keeps per-frame cost roughly linear in Cα count.

Benchmarks are reproducible via the built-in benchmark mode, which replays a fixed key script and logs per-frame timings to a structty_bench_*.csv file.

Third-Party Libraries

LibraryLicensePurpose
GemmiMPL-2.0mmCIF/PDB file parsing
stb_image_writeMIT / public domainPNG screenshot encoding

See THIRD_PARTY_NOTICES.md for detailed license information.

License

This project is licensed under the MIT License.


Developed by Luna Sung-eun Jang, Soo Young Cha — Steinegger Lab