Chapter 16 — Spatial Transcriptomics
Where this chapter sits. Companion to Lecture 16 and Tutorial 16. Prerequisites: Chapter 1 — Preprocessing (general QC), Chapter 2 — DimReduction & Clustering (the geometry workflow generalizes).
16.1 What spatial buys us
scRNA-seq starts with dissociation: tissue → cell suspension. That gives each cell’s transcriptome but loses everywhere it ever was in the tissue. For developmental biology, immunology, and disease microenvironments, position is the answer to half the questions:
- Where in a tumor are exhausted T cells located? Adjacent to which tumor sub-clones?
- In a developing kidney, which transcriptional state precedes which?
- Are immune cells in the ductal periphery or the lobular core? Granular and scattered, or concentrated in foci?
- Which genes are expressed only at tissue boundaries, where dissociation would shred the spatial signal?
Spatial transcriptomics measures gene expression while preserving the position. The array-based approach that launched the modern field1 became 10x Visium; the major platforms now differ in resolution (spot vs cell vs sub-cellular) and coverage (whole-transcriptome vs targeted panel). Several reviews map the rapidly expanding landscape2–4.
16.2 The technology landscape
Two axes define the spatial transcriptomics landscape: resolution (spot vs. single-cell vs. sub-cellular) and coverage (whole-transcriptome vs. targeted gene panel). Visium is high-coverage / low-resolution; Xenium and MERFISH are targeted / high-resolution. Match the platform to your hypothesis. Table 1 summarizes the major current options.
| Technology | Resolution | Throughput | Genome coverage | Best for |
|---|---|---|---|---|
| 10x Visium (v1, v2) | 55 µm spots | ~5,000 spots / section | Whole transcriptome | First spatial study; tissue overview |
| 10x Visium HD | 2 µm bins (sub-cellular) | ~6 M bins / section | Whole transcriptome | High-res Visium replacement (2023+) |
| 10x Xenium | Single-cell, sub-cellular | 100k–500k cells | 200–5000 gene panel | Single-cell-resolution spatial; targeted panels |
| MERFISH (Vizgen)5 | Sub-cellular | 100k–1M cells | Up to ~1000 genes | High plex, single-cell resolution |
| NanoString CosMx | Sub-cellular | similar to MERFISH | ~1000 genes (extending to whole-tx) | Clinical samples, FFPE |
| Slide-seq / Slide-tags | 10 µm | ~50k beads | Whole-tx | Higher resolution than Visium, less spatial-uniform |
16.3 What a Visium spot is — and isn’t
A Visium spot is a 55 µm circle on the tissue surface that captures the polyA-tailed mRNA from whatever happened to be under it. Depending on the tissue:
- A 55 µm spot in mouse brain captures ~5–10 cells
- A 55 µm spot in dense tumor tissue captures ~10–15 cells
- A 55 µm spot in adipose tissue captures ~1–3 cells
So a spot is not a single cell. Most analysis tooling reuses the language of single-cell (clusters, marker genes, integration) but each “cell” is really a small neighborhood of cells. This shapes everything downstream:
- Clustering recovers tissue regions, not cell types directly
- DE between spot-clusters reveals region-vs-region differences
- “Deconvolution” is its own analytical step that decomposes each spot into cell-type fractions
For true single-cell resolution, use Xenium / MERFISH / Visium HD. For tissue-overview-level analysis, Visium is fine and cheaper.
The 55 µm diameter of a Visium spot is larger than most mammalian cells (5–20 µm). This means every spot captures a mixture of cell types from the tissue neighborhood directly beneath it. The transcriptome of a spot is a weighted average of the transcriptomes of its constituent cells, weighted by how much mRNA each cell contributed. This has two important consequences. First, marker genes for a single cell type will be “diluted” by the expression of co-localized cells — a spot that is 80% neurons and 20% astrocytes will express both neuronal and astrocytic markers, making it impossible to assign it cleanly to one cell type without deconvolution. Second, clustering of spots recovers anatomical regions (layers, tissue compartments) rather than individual cell types, because spots in the same anatomical region contain similar mixtures of the same cell types. These properties make Visium well-suited for tissue-architecture questions and poorly suited for questions that require single-cell resolution.
The Seurat object produced by Load10X_Spatial() stores the Visium data differently from a standard scRNA-seq object. The @images slot holds a VisiumV1 object containing four things: the H&E histology image (a downsampled JPEG from the Space Ranger output), the per-spot tissue coordinates (the pixel position of each spot’s center in the image), the scale factors (ratios between the full-resolution, low-resolution, and high-resolution coordinate systems that Space Ranger uses), and a flag for which barcodes are “in tissue” (spots that landed on tissue vs. the background). SpatialFeaturePlot() uses all four components simultaneously: it places colored circles at the correct pixel coordinates, scales them to the spot size using the scale factors, and overlays them on the H&E image. If any component is missing or misaligned, the plot will either error or produce circles that float in the wrong positions relative to the tissue.
16.4 The Visium workflow
The lab pipeline:
- Tissue prep. Fresh-frozen sections, 10 µm thick, mounted on Visium slide
- H&E stain + image the slide (this is the histology image stored in the Seurat object)
- Permeabilize the tissue so mRNA can release through the spots
- 10x Visium chip captures the released mRNA on barcoded primers per spot
- Sequence as standard 10x
The bioinformatic pipeline:
- Load the
spacerangeroutput into Seurat. The resulting object’s@imagesslot holds aVisiumV1object containing four things: the histology image (low-resolution JPEG), the per-spot tissue coordinates (which pixel each spot maps to in the image), the scale factors (ratios between the full-res, low-res, and hi-res coordinate systems), and a flag for which barcodes are “in tissue”.SpatialFeaturePlotuses all four to draw expression over the H&E. - QC per spot — same metrics as scRNA (UMI count, gene count, percent.mt) plus always plot them on the tissue, because spatial outliers are tissue artifacts (folds, bubbles), not random noise.
- Normalize with
SCTransform— preferred overLogNormalizefor spatial because each spot captures the transcriptome of ~1–10 cells (tissue-dependent) rather than one. This wider dynamic range means count distributions are further from the Poisson assumption thatLogNormalizeimplicitly relies on;SCTransform’s regularized negative binomial model adjusts per-spot dispersion and handles outlier spots more robustly.
SCTransform(brain, assay = "Spatial", verbose = FALSE) runs the regularized negative-binomial regression on Visium counts. Default behavior: estimates per-gene overdispersion parameters (θ) by pooling information across genes, then extracts Pearson residuals as the normalized values. For Visium, the key difference from scRNA-seq is that the assay argument must be "Spatial" (not "RNA"). Visium spots have systematically wider count distributions than single cells because each spot aggregates multiple cells — SCTransform handles this by fitting per-spot size factors. A common warning, “iteration limit reached,” is usually safe to ignore for Visium: it occurs when an outlier spot causes the NB regression to converge slowly, but the result is still usable. Unlike scRNA-seq, SCTransform for Visium also accounts for the spatial variation in sequencing depth that arises from tissue-section-edge effects, making it more robust than LogNormalize for spatial data.
- Run PCA + clustering as in scRNA — but expect clusters to recover anatomical regions, not single-cell-type populations.
- Spatially variable features with Moran’s I — see §16.6.
- Integrate sections if you have multiple — same Harmony / anchor methods as scRNA, using the SCT integration path.
- Deconvolute to get cell-type fractions per spot (§16.7).
16.5 Spatial QC — always plot metrics on the tissue
For scRNA the QC distribution alone is informative. For spatial, you need both:
- The distribution (violin or histogram per sample)
- The spatial overlay — color the tissue by the metric
The latter catches:
- Tissue folds — spots in a fold get fewer reads; their UMI counts are low. Don’t filter blindly; recognize the spatial pattern.
- Edge artifacts — outermost spots only partially cover tissue; UMI counts drop on the periphery
- Bubble/dropout regions — localized regions of very low UMI; technical artifact
Filter spots by their distribution with this spatial context in mind. The right threshold for a brain section may exclude the white matter (which is biologically lower-signal) — check that the filtered region matches a real anatomical structure, not just a number.
16.6 Spatially variable features and Moran’s I
Some genes vary spot-to-spot with spatial structure — clear pattern on the tissue. Some vary spot-to-spot but spatially randomly. The first kind is what you want to study in a spatial dataset.
Moran’s I is the standard spatial-autocorrelation statistic:
\[I = \frac{N}{W} \cdot \frac{\sum_i \sum_j w_{ij}(x_i - \bar x)(x_j - \bar x)}{\sum_i (x_i - \bar x)^2}\]
where \(w_{ij}\) is a spatial weight (large for spatially close spots, zero for far). High Moran’s I = adjacent spots have similar expression = spatially structured gene.
Seurat’s FindSpatiallyVariableFeatures runs the test. The output is the gene list ranked by Moran’s I; the top hits are genes whose spatial pattern is most non-random (spatial markers, anatomical-region-specific genes).
Compare to FindVariableFeatures (which finds variable-but-not-necessarily-spatially-structured genes): the spatially variable list is a strict subset of the variable list. The difference is informative — variable but spatially random genes are usually technical (depth, ambient bleed) rather than biological.
FindSpatiallyVariableFeatures(brain, assay = "SCT", features = VariableFeatures(brain)[1:1000], selection.method = "moransi") identifies spatially autocorrelated genes. Default selection.method: "markvariogram" (a variogram-based approach) but the tutorial uses "moransi" because it is faster and more interpretable. "moransi" computes Moran’s I for each gene and ranks by the statistic; "markvariogram" fits a spatial variogram model and uses the fitted range parameter. For a first analysis, "moransi" is preferred because Moran’s I has a well-defined statistical interpretation and is faster. The features argument limits computation to the top HVGs — running on all 32,000 genes in the brain dataset is feasible but slower; restricting to VariableFeatures(brain)[1:1000] covers the biologically relevant range. For a thorough publication analysis, run on all VariableFeatures or the full gene set.
FindVariableFeatures() (used in scRNA-seq) finds genes that vary substantially across spots/cells, but variation can be driven by technical noise, depth heterogeneity, or genuine biological heterogeneity unrelated to spatial position. FindSpatiallyVariableFeatures() adds a spatial criterion: the variation must be autocorrelated in space, meaning nearby spots tend to share similar values. A gene with high Moran’s I has a spatial pattern — it is high in one region of the tissue and low in another. A gene with low Moran’s I but high total variance is spatially random, which often indicates a technical artifact or a rare cell type distributed uniformly. The spatially variable gene list is the biologically richer starting point for anatomical marker discovery and spatial domain analysis.
16.7 Cell-type deconvolution
Each Visium spot is a mixture. Deconvolution decomposes each spot’s transcriptome into estimated cell-type fractions, using a single-cell reference of the same tissue.
Tools differ in their statistical model and whether they incorporate spatial smoothing; Table 2 summarizes the main options. The reference choice is critical regardless of tool: deconvolving a brain Visium with a PBMC reference returns garbage (PBMCs don’t include neurons). Match the reference to the tissue you sampled.
run.RCTD(rctd, doublet_mode = "doublet") runs the RCTD deconvolution in doublet mode, which allows each spot to contain at most two cell types. Default doublet_mode = "doublet" is appropriate for Visium, where many spots genuinely contain exactly two dominant cell types. Alternative modes: "full" (allows any number of cell types per spot, more flexible but noisier), "multi" (explicitly models multi-type spots, computationally expensive). For the reference, RCTD requires at minimum ~25 cells per cell type — cell types represented by fewer cells in the reference will have unreliable deconvolution estimates. The reference cell type set should include all cell types plausibly present in the tissue; missing reference cell types will cause their cells to be mis-assigned to the nearest available cell type.
| Tool | Method | Notes |
|---|---|---|
RCTD / spacexr6 |
Maximum likelihood with cell-type-specific platform effects | Workshop default |
| CARD | Conditional autoregressive prior on neighborhood smoothness | Smoother results |
| cell2location7 | Bayesian, Python | Atlas-scale; pairs with HCA |
| SPOTlight | Non-negative matrix factorization | Good when you don’t have a great reference |
| CIBERSORTx | Support-vector regression | Originally bulk; works on spatial |
The output: per-spot, a probability vector over cell types. Plot each cell-type fraction as a SpatialFeaturePlot; you see the spatial distribution of each cell type as it’s distributed across the tissue.
16.8 Spatial-aware clustering — beyond standard SNN+Leiden
Standard Seurat clustering treats spots as iid points in expression space. But adjacent spots are spatially correlated by construction — a clustering that ignores spatial layout misses that signal.
Modern spatial-aware clusterers:
- BANKSY — augments each spot’s expression with its neighbors’ average expression before clustering
- STAGATE — graph attention auto-encoder on the spatial graph; learns smoother spatial domains
- SpaGCN — graph convolutional network combining expression + histology image
- GraphST — contrastive self-supervised graph learning
These produce smoother, more biologically interpretable spatial domains. Worth using for any “what are the regions of this tissue?” question.
16.9 Common errors
Table 3 collects the errors most commonly encountered in a Seurat Visium workflow, their causes, and the fix.
| You see | What’s wrong | What to do |
|---|---|---|
SeuratData::InstallData("stxBrain") is slow |
First-time download (~50 MB) | Subsequent runs are instant |
SCTransform warns “iteration limit reached” |
NB regression hits its iteration limit on outlier spots | Usually safe to ignore; results are still usable |
SpatialFeaturePlot shows nothing on the tissue |
The images slot wasn’t loaded |
Re-run Load10X_Spatial(), or attach the image with Read10X_Image() pointing at the Space Ranger spatial/ folder |
| Section integration OOMs | SCT-anchor integration is RAM-hungry on Visium | Downsample spots first, or use Harmony on SCT residuals |
| Deconvolution returns flat per-cell-type fractions | Your reference doesn’t match the tissue | Use a tissue-matched single-cell reference |
16.10 Where this material is also discussed
- Lecture: Lecture 16
- Tutorial: Tutorial 16
- scNotebooks Module 10 — parallel walkthrough on 10x Visium breast cancer
16.10b Additional design considerations
Cell–cell communication in space. Standard CellChat (Chapter 14) asks which ligand–receptor pairs are co-expressed between clusters but ignores whether the sender and receiver cells are spatially adjacent. LIANA+ (Python) extends the LIANA framework to spatial data by weighting predicted interactions by the physical proximity of spots, substantially reducing false positives from cell types that are co-expressed but never co-localized.
Image-aware normalization. Standard SCTransform does not account for tissue-section-edge effects, where spots at the periphery of the section are only partially covered and therefore show systematically lower UMI counts. STUtility and its successor semla add a tissue-image-based normalization step that corrects for spatial gradients in coverage before downstream analysis — particularly useful for sections with irregular edges or tissue damage.
Spot-level percent.mt pattern. In mouse brain, mitochondrial percentage typically varies systematically: it can be elevated at tissue edges (partial spots, damaged cells), and some cell types (neurons under stress) show higher baseline mitochondrial expression. Before filtering on percent.mt, plot it spatially; a ring of elevated signal around the section periphery is expected and does not warrant removing interior spots.
16.11 Going further
For the technology and its history, start with the array-based method paper1 and the review trilogy2–4; MERFISH5 represents the imaging-based, single-cell-resolution branch. For analysis, the deconvolution tools the chapter recommends are RCTD6 and cell2location7, and squidpy8 is the scalable Python framework for spatial-neighborhood analysis; BANKSY (Singhal et al. 2024) is a strong spatial-aware clusterer. The full curated list is on Key Papers, Reviews & Benchmarks.