An interactive spatial transcriptomics viewer for exploring cell-cell communication from NICHESv2 directly on the tissue image.
π User manual β step-by-step setup and a reference for every control in the interface. No programming experience assumed.
Spatial transcriptomics platforms (Xenium, seqFISH, Visium, Visium HD, MERSCOPE, CosMx) produce high-resolution images with hundreds of genes measured per cell. NICHESv2 infers which cells are communicating and through which ligand-receptor mechanisms (LRMs). TissuePlex bridges those two outputs: it overlays the NICHESv2 communication graph on the tissue image and lets you explore it interactively.
Key capabilities:
- Toggle individual LRMs in real time β select any subset of 100s of ligand-receptor mechanisms and instantly see which cell pairs are communicating through them
- Color edges by communication score or metadata β visualize LRM set strength, cell type, or any custom column from your analysis as a continuous or categorical color scale
- Click any edge for full detail β inspect every active LRM for a given cell pair with their individual scores
- Directed edges with arrowheads β AβB and BβA are visually distinct; autocrine communication renders as a ring per cell, on the same color scale and mechanism filter as the edges
- Multiple edge sets per dataset β drop several
.parquetfiles into anedges/folder and flip between scoring approaches on the same tissue without duplicating the image or cell data - Pan and zoom on high-resolution morphology images β OME-TIFF tile pyramid with smooth zoom from whole-tissue to single-cell scale
- Multi-channel morphology β Xenium
morphology_focus/channels are selectable alongside the top-level morphology image - Cross-platform metadata β the
cell-metadata/convention works the same way on every platform that supports it, so annotation workflows transfer between Xenium and seqFISH unchanged - Split-screen comparison β two independently navigable panels sharing one set of layer controls, with a match-zoom button
- Per-panel rotation β rotate either panel to any angle to align tissue orientation
- Transcript dot overlay β per-gene colored dots, filterable by gene species, with hover tooltips
- Cell/spot segmentation β polygon boundaries with color-by-gene-set or color-by-metadata, and editable per-category colors
- Autocrine signalling β self-signalling drawn as a ring per cell, colored by the same scale as the directed edges and obeying the same mechanism filter
- Metadata filtering β restrict the view to a subset of cells or edges (a sample, a few cell types, a value range). Applied server-side before sampling, so a rare cluster renders at full density instead of being sampled away
- Treat-as-categorical toggle β integer-coded cluster IDs get a discrete editable palette rather than a viridis gradient, with the numeric order preserved in the legend
- Region drawing and measurement tools β annotate areas, export cell selections, save PNG screenshots
- Supplemental metadata β drop any CSV or parquet into a
cell-metadata/folder to add custom color-by columns (clusters, pseudotime, etc.) without touching the original data - Multi-dataset support β switch between datasets without restarting; each is auto-detected by platform
| Platform | Vendor | Morphology | Transcripts | Cell segments | Edges |
|---|---|---|---|---|---|
| Xenium | 10x Genomics | β | β | β | β |
| seqFISH | Spatial Genomics | β | β | β | β |
| Visium HD | 10x Genomics | β | β | β (bins) | β |
| Visium | 10x Genomics | β | β | β (spots) | β |
| MERSCOPE | Vizgen | β | β | β | β |
| CosMx | Nanostring | placeholder | β | β | β |
All nine bundled demo datasets ship edges.parquet and an edge-metadata/ folder, so the connectivity layer and the annotation workflow have something to show on every platform. Two caveats worth knowing: the MERSCOPE panel carries only 2 complete ligand-receptor pairs and the seqFISH reference panel none, because targeted panels are chosen for cell typing rather than signalling β check_lr_coverage() reports this before NICHESv2 runs, and seqfish_instrument2 therefore uses synthetic edges from make_edges.py. The edge-metadata/ folders are generated demo annotations, not analysis output; each carries a README saying which columns are derived and which is invented.
seqFISH means the commercial Spatial Genomics GenePS output, not the academic seqFISH/seqFISH+ method, which has no standard file layout; the current v2 layout is fully supported, and legacy v1 reads cells and transcripts but not boundaries.
The two array-based platforms have no per-molecule transcript coordinates β only spot- or bin-level UMI counts β so they synthesise a polygon per unit instead: a square for a Visium HD bin, a circle for a 55 Β΅m Visium spot. Fill, colour-by, picking and region selection then all work through the same layers. CosMx datasets often ship no morphology image, in which case TissuePlex renders the data onto a blank canvas sized to the tissue. Each reader declares what it supports via a capability flag, and the UI hides layers the platform cannot serve β and relabels itself, so a Visium dataset says "spot" wherever a Xenium one says "cell".
The edge connectivity layer (NICHESv2 output) works with any platform β it is platform-agnostic as long as cell barcodes match.
Requirements: Docker Desktop (Mac/Windows) or Docker Engine + Compose v2 (Linux). Nothing else needed on the host.
git clone https://github.com/RaredonLab/TissuePlex.git
cd TissuePlex
docker compose up --buildOpen http://localhost:3000.
DATA_PATH=/absolute/path/to/your/datasets docker compose up --buildDATA_PATH should be a parent folder containing one or more platform output directories. TissuePlex auto-detects the platform from each subdirectory's contents:
/your/datasets/
xenium_run_A/
experiment.xenium β Xenium sentinel
morphology.ome.tif
morphology_focus/ β optional; extra channels appear in the image picker
cells.parquet
transcripts.parquet
cell_boundaries.parquet
edges.parquet β NICHESv2 output (optional)
edges/ β optional; additional edge sets to flip between
raw_minimum.parquet
normalized_product.parquet
seqfish_run_B/ β Spatial Genomics GenePS; one ROI per folder
Roi1_CellCoordinates.csv β seqFISH sentinel
Roi1_CellxGene.csv
Roi1_TranscriptList.csv
Roi1_Boundaries.geojson
Roi1_DAPI.tiff
edges.parquet
visium_hd_run_C/ β Space Ranger outs/
binned_outputs/
square_008um/ β Visium HD sentinel
filtered_feature_bc_matrix.h5
spatial/{tissue_positions.parquet, scalefactors_json.json, tissue_hires_image.png}
edges.parquet
visium_run_D/
filtered_feature_bc_matrix.h5
spatial/ β Visium sentinel: scalefactors_json.json +
scalefactors_json.json tissue_positions.csv
tissue_positions.csv
tissue_hires_image.png
edges.parquet
merscope_run_E/
cell_by_gene.csv β MERSCOPE sentinel
cell_metadata.csv
detected_transcripts.csv
edges.parquet
cosmx_run_F/
my_experiment_tx_file.csv β CosMx sentinel
edges.parquet
If edges.parquet is absent the edge layers are hidden β all other layers work normally. When a dataset has more than one edge source, a dropdown appears at the top of the Edge Data section; the selection applies to every open panel.
The first launch builds DZI tile pyramids from OME-TIFF morphology images. This takes ~30 seconds per dataset and is cached across restarts.
docs/cloud-deploy.md is a step-by-step DigitalOcean runbook (~$106β116/month) covering droplet setup, block storage for data, DNS, automatic TLS via Caddy, and data upload. Note that access control is opt-in: unless you enable Caddy's basicauth, anyone with the URL can view the data.
TissuePlex is designed as a downstream visualization step for NICHESv2. After running NICHESv2 on your spatial dataset, export the connectivity object:
# In R, after running NICHESv2:
export_to_TissuePlex(
niches_object,
output.path = "/your/datasets/xenium_run_A/edges.parquet",
celltype.col = "Type.6"
)Working examples of the full pipeline β exporting Seurat metadata, running NICHESv2, and exporting the parquet β are in r/. Those scripts have hardcoded paths and are meant to be read and adapted, not run as-is.
Then launch TissuePlex β the edge layer will appear automatically.
The edges.parquet format is one row per (directed edge) Γ (LRM). AβB and BβA are separate rows. Any additional columns in the file (cell types, scores, custom metadata) are automatically available as color-by options in the UI. See docs/data_format.md for the full column specification.
To add custom annotation columns (clusters, pseudotime, leiden labels, etc.) to the cell color-by menu without modifying the original platform output:
dataset_folder/
experiment.xenium
cells.parquet
cell-metadata/ β create this directory
clusters.csv β barcodes in first column (or cell_id column)
pseudotime.parquet
Standard R export works out of the box:
write.csv(my_metadata, file.path(dataset_dir, "cell-metadata", "metadata.csv"))Columns appear automatically in the Cell Color and Cell Filter dropdowns. Continuous columns get a gradient; string and low-cardinality integer columns get discrete colors. Use treat as categorical to override that guess either way β a Seurat cluster column with more than 30 levels still gets discrete colors, and a coded column you want as a gradient can have one.
The same idea for cell pairs β annotate edges without regenerating edges.parquet from R:
dataset_folder/
edges.parquet
edge-metadata/ β create this directory
annotations.csv β key column `edge` = "SendingCell|ReceivingCell"
write.csv(annotations_df, file.path(dataset_dir, "edge-metadata", "annotations.csv"))Columns appear automatically in the edge color dropdown, and show as an Annotations block when you click an edge. The folder sits beside the dataset rather than beside the edge file, so one set of annotations applies across every edge source β annotations describe cell pairs, which belong to the tissue rather than to one scoring run.
sample_data/mouse_ileum_tiny ships worked examples of both cell-metadata/ and edge-metadata/.
# Backend (FastAPI + DuckDB)
cd backend
pip install -r requirements.txt
DATA_ROOT=../sample_data uvicorn app.main:app --reload
# Frontend (React + Vite) β in a separate terminal
cd frontend
npm install
npm run dev # β http://localhost:5173, proxies /api β :8000The dev server uses port 5173 so it does not collide with docker compose, which binds 3000 for the production frontend.
There is currently no automated test suite, CI, or linter β changes are verified by running the app.
Browser
OpenSeadragon β pan/zoom over OME-TIFF tile pyramid
deck.gl (WebGL) β all data layers; coordinate-synced to OSD
FastAPI backend
/tiles β OME-TIFF β DZI tile pyramid (pyvips / tifffile fallback)
/spatial β transcripts, cell boundaries, cell metadata, gene expression
/edges β edge query, LRM catalogue, per-edge color values, edge detail
/layers β generic parquet layer serving
All data is served directly from parquet files via DuckDB β no database setup or import step. Tile pyramids are built on first access and cached.
- Create
backend/app/readers/my_platform_reader.pyextendingSpatialDatasetReader - Implement the abstract methods (
cells,transcripts,cell_boundaries, etc.) - Override
capabilities()to declare which layers the platform supports - Register a detector in
reader_factory.py
The frontend automatically adapts its layer controls to the capabilities your reader declares.
Tracked in GitHub issues.
#45 (select by metadata) and #35 (treat-as-categorical toggle) are both implemented. Natural follow-ups, neither yet built:
- Combining more than one filter at a time β today it is one column, so "cluster 4 and sample B" needs two passes
- Persisting the categorical choice and the palette across reloads
- Filtering transcripts, which needs a transcriptβcell assignment that several platforms do not ship
Large datasets are handled by a spatial index built automatically on first access, alongside the tile pyramid: files over 64 MB are rewritten sorted by a spatial grid, which makes viewport queries roughly 27Γ faster on a 40M-row transcript file and also converts seqFISH CSV to parquet along the way. Set SPATIAL_CACHE=0 to disable it.
If you use TissuePlex in published work, please cite: (preprint / paper link β coming soon)
