Vol. 01 · No. 1 · Beta Edition

RNA Velocity Tutorial

Infer the future transcriptional state of individual cells by measuring spliced and unspliced mRNA ratios using scVelo — four workflow templates covered.

Intermediate·RNA Velocity

What you'll learn

  • Load spliced/unspliced count layers from H5AD, Velocyto loom, or STARsolo Velocyto MTX output and run scVelo's velocity model.
  • Choose between stochastic (moment-based) and dynamical (likelihood-based) modes and understand the trade-offs.
  • Render velocity arrows / streamlines on UMAP and recover a transcription-rate-aware latent time for developmental ordering.

Prerequisites

  • • Oreumics installed (install guide).
  • • PBMC tutorial recommended — you should already understand UMAP and clustering before adding velocity.
  • • Spliced, unspliced, latent time, and terminal state are defined in the glossary at the bottom.
  • • Sample data: sampledata/velocity/dentategyrus_velocity.h5ad (Templates 1 & 2), velocyto_demo.loom (Template 3), star_velocyto/filtered/ (Template 4) — all pre-bundled.

Overview

RNA velocity leverages the ratio of unspliced (nascent) to spliced (mature) mRNA to predict in which direction — and how fast — each cell is moving through transcriptional space. Oreumics wraps scVelo and supports three computational modes: stochastic (moment-based, the default), dynamical (likelihood-based, most accurate, slowest), and deterministic (original La Manno method). This tutorial covers all four input workflows: loading from a pre-processed H5AD, running latent time analysis, loading a Velocyto loom file, and loading directly from STARsolo output. Each one is a shipped template: RNA Velocity Stream (H5AD), RNA Velocity Latent Time, RNA Velocity from Velocyto Loom, and RNA Velocity from STARsolo.

Choose your starting point

All four templates produce velocity arrows on a UMAP. Start with Template 1 (H5AD) to learn the basics, then graduate to Template 2 (Latent Time) for the most biologically interpretable output.

Datasets

Sample files (pre-packaged in Oreumics)

sampledata/velocity/dentategyrus_velocity.h5ad— Templates 1 & 2: 2,930 cells × 13,913 genes from mouse dentate gyrus (hippocampal neurogenesis; Hochgerner et al. 2018 via scVelo), spliced/unspliced/ambiguous layers, a ready UMAP in obsm["X_umap"] and 14 cell-type labels in obs["clusters"]
sampledata/velocity/velocyto_demo.loom— Template 3: small Velocyto .loom demo file (spliced/unspliced/ambiguous layers)
sampledata/velocity/star_velocyto/filtered/— Template 4: STARsolo Velocyto output directory (spliced/unspliced/ambiguous .mtx.gz + features/barcodes)
Template 1

RNA Velocity Stream (H5AD)

The fastest route to velocity arrows. Uses the dentate gyrus neurogenesis dataset (included in scVelo) pre-packaged as an H5AD with spliced/unspliced layers already present. Four nodes.

Pipeline

Load H5AD
→
RNA Velocity
→
Velocity Graph
→
Velocity Visualization
Load H5ADInput / Output

Load the dentate gyrus velocity dataset. The spliced and unspliced layers are picked up automatically. If you are using your own H5AD, ensure it contains at least adata.layers["spliced"] and adata.layers["unspliced"] — these are produced by Velocyto or STARsolo.

file_pathsampledata/velocity/dentategyrus_velocity.h5ad

What you'll see

AnnData with 2,930 cells × 13,913 genes. Layers: spliced, unspliced, ambiguous. obs["clusters"] holds 14 annotated populations (Neuroblast, Granule immature/mature, Radial Glia-like, nIPC, OPC, Astrocytes, Microglia, …) and obsm["X_umap"] is already present.

RNA VelocityRNA Velocity

Fit the stochastic velocity model to each gene independently. The stochastic mode uses a moment-based approach and is the quickest of the three. min_shared_counts: 20 filters out genes where fewer than 20 cells have both spliced and unspliced reads (unreliable fit). n_top_genes: 2000 limits computation to the most variable genes for speed without sacrificing accuracy on the major axes; n_neighbors / n_pcs control the moment computation.

modestochastic
n_top_genes2000
n_neighbors30
n_pcs30
min_shared_counts20

What you'll see

Velocity values stored in adata.layers["velocity"]. Gene-level fit statistics in adata.var["velocity_genes"] — check this column to see which genes drove the fit.

Velocity GraphRNA Velocity

Compute the cell-to-cell transition probability graph. With approx: true, scVelo uses approximate nearest-neighbor search instead of an exact one, which is considerably faster on large datasets. The resulting graph encodes the probability that each cell transitions to each of its neighbors in the direction implied by its velocity vector.

approxtruefaster; recommended for larger datasets
Velocity VisualizationVisualization

The stream plot interpolates velocity arrows into continuous streamlines on the UMAP embedding. density: 1.0 fills the UMAP uniformly with streamlines. smooth: 0.5 applies moderate Gaussian smoothing to suppress noise without hiding real directionality.

plot_typestream
basisumap
color_byclusters
density1.0
smooth0.5

What you'll see

UMAP with colored streamlines. Arrows flow from progenitors (Radial Glia-like, nIPC, Neuroblast) toward mature neurons (Granule immature → Granule mature). A clear "fountain" pattern — arrows pointing away from progenitors — confirms a successful velocity run.

Template 2

RNA Velocity Latent Time

The dynamical model fits a full kinetic model to splicing dynamics and recovers a transcription-rate-aware “latent time” — a continuous pseudotime independent of clustering. Markedly slower to compute than the stochastic model, but the standard choice for developmental datasets.

Pipeline

Load H5AD
→
RNA Velocity
→
Velocity Graph
→
Latent Time
→
Terminal States
→
Velocity Visualization
Load H5ADInput / Output

Same file as Template 1. The dentate gyrus dataset works well for the dynamical model because it has a clear developmental trajectory.

file_pathsampledata/velocity/dentategyrus_velocity.h5ad
RNA VelocityRNA Velocity

The dynamical model jointly estimates transcription rate (α), splicing rate (β), and degradation rate (γ) for each gene using an expectation-maximization algorithm. This is the most accurate mode and by far the slowest; the run can be cancelled from the execution overlay if you need to change parameters.

modedynamicalslower but most accurate
n_top_genes2000
n_neighbors30
n_pcs30
min_shared_counts20

What you'll see

Gene-level kinetic parameters (fit_alpha, fit_beta, fit_gamma, fit_t_) stored in adata.var. Only genes with a good likelihood are retained as velocity genes.

Tip

Dynamical mode gives a proper latent time; stochastic mode does not. For large datasets, subset cells with Subset Data first to shorten the EM fitting.

Velocity GraphRNA Velocity

Compute transition probabilities (approx: true). With the dynamical model, the velocity vectors are more accurate, so the graph is also more reliable. The graph is stored in adata.uns["velocity_graph"].

approxtrue
Latent TimeRNA Velocity

Latent time is recovered from the dynamical model's gene-specific time parameter — it represents absolute transcriptional progress rather than a rank-based pseudotime. min_likelihood: 0.1 filters genes that did not fit the kinetic model well before computing the latent time consensus. root_key / end_key are auto-detected when left empty.

min_likelihood0.1exclude poorly-fitted genes

What you'll see

adata.obs["latent_time"] populated with values in [0, 1]. Cells at t=0 are transcriptionally earliest (progenitors); cells approaching t=1 are most mature. Unlike DPT, latent time is computed without a pre-defined root cell.

Terminal StatesRNA Velocity

Identify terminal (end-point) and root cells from the velocity transition matrix with scVelo's terminal_states routine (this is not CellRank — for GPCCA macrostates and fate probabilities use the CellRank Kernel / Cell Fate Analysis nodes). groupby: clusters summarises the result per cluster so it is easy to read.

groupbyclusters
self_transitionsfalse

What you'll see

adata.obs["terminal_states"] added. In the dentate gyrus data the end points should fall in the mature populations (e.g. Granule mature) and the root among progenitors (Radial Glia-like / nIPC). Check that this matches your biological expectation.

Velocity VisualizationVisualization

The latent_time plot produces a UMAP colored by the latent_time value, with a color gradient from early to late. Unlike the stream plot, this shows temporal ordering without arrows — cleaner for publication.

plot_typelatent_time
basisumap
color_byclusters

What you'll see

UMAP with a smooth gradient tracing neurogenesis from progenitor zones to mature granule cells. The gradient should align with the stream arrows from Template 1, validating both methods.

Template 3

RNA Velocity from Velocyto Loom

If you ran Velocyto on your BAM files directly, the output is a per-sample .loom file. This template loads that file directly and runs stochastic velocity. The pipeline is otherwise identical to Template 1.

Pipeline

Load Velocyto Loom
→
RNA Velocity
→
Velocity Graph
→
Velocity Visualization
Load Velocyto LoomInput / Output

The Velocyto loom format carries three layers: spliced, unspliced, and ambiguous. The loader maps them onto the standard AnnData layer names and strips the "sample:" prefix Velocyto adds to cell barcodes. The node has a single parameter. If you have multiple loom files (one per sample), load each with its own node and concatenate them with Merge Data before the RNA Velocity node.

file_pathsampledata/velocity/velocyto_demo.loom

What you'll see

AnnData loaded with the correct spliced/unspliced/ambiguous layers. Check the node card to confirm the layer shapes (cells × genes for each layer).

RNA VelocityRNA Velocity

A lower min_shared_counts (10 vs 20 in Template 1) is appropriate for smaller datasets or datasets with sparser coverage, where fewer cells will have high shared counts. The stochastic model is recommended for loom-derived data unless you have very deep sequencing.

modestochastic
n_top_genes2000
n_neighbors30
n_pcs30
min_shared_counts10lower than H5AD template for sparser data
Velocity GraphRNA Velocity

Compute transition probability graph with default settings. No changes needed from Template 1.

approxtrue
Velocity VisualizationVisualization

If your loom file already carries UMAP coordinates in adata.obsm["X_umap"], they are used directly. Otherwise, run Neighbors and UMAP nodes before this node to generate a fresh embedding (the RNA Velocity node computes its own moments but does not write a UMAP).

plot_typestream
basisumap
density1.0
smooth0.5

What you'll see

Stream velocity plot on UMAP. Arrow directions should be biologically consistent — if arrows appear random, check that the cell barcodes in your loom file match those in your AnnData (barcode format can differ between Velocyto, STARsolo, and Cell Ranger).

Template 4

RNA Velocity from STARsolo

STARsolo (run with --soloFeatures Velocyto) can output spliced/unspliced/ambiguous count matrices directly without running Velocyto separately. This template loads those matrices and renders an arrow-style velocity plot.

Pipeline

Load STARsolo Velocity
→
RNA Velocity
→
Velocity Graph
→
Velocity Visualization
Load STARsolo VelocityInput / Output

Point the node at the STARsolo Velocyto filtered/ folder containing spliced.mtx(.gz), unspliced.mtx(.gz), ambiguous.mtx(.gz) plus features and barcodes. The loader reads all three matrices, transposes them (STARsolo stores genes × cells, not cells × genes), and merges them into a single AnnData with the correct layer names. There is only one parameter.

directory_pathsampledata/velocity/star_velocyto/filtered/

What you'll see

AnnData with three layers (spliced, unspliced, ambiguous) derived from the STARsolo Velocyto output. The cells × genes shape should match the expected cell count from your experiment.

RNA VelocityRNA Velocity

STARsolo unspliced counts tend to be sparser than Velocyto loom counts because STARsolo uses a stricter read assignment rule. A lower min_shared_counts threshold (10 instead of 20) compensates for this sparsity. If you find too few velocity genes after fitting, try lowering min_shared_counts further to 5.

modestochastic
n_top_genes2000
n_neighbors30
n_pcs30
min_shared_counts10STARsolo unspliced is sparser
Velocity GraphRNA Velocity

Compute transition probabilities with approx: true. Same as other templates.

approxtrue
Velocity VisualizationVisualization

The arrow plot places individual velocity arrows on cells rather than interpolated streamlines. This is more detailed than the stream plot and useful for inspecting velocity at single-cell resolution. arrow_length and arrow_size tune the arrows for grid/arrow plots (stream ignores them). The synthetic demo folder has no stored UMAP, so add Neighbors and UMAP before this node.

plot_typearrowper-cell arrows instead of streamlines
basisumap
density1.0
smooth0.5

What you'll see

UMAP with individual arrows at each cell. Coherent arrow fields (all pointing the same direction in a region) indicate strong, consistent velocity signal. Noisy, random-direction arrows suggest the fit was poor — typically because those cells have very low unspliced counts.

Expected Results Summary

  • • Template 1 (stream): dentate gyrus velocity stream shows a clear neurogenic trajectory from progenitors (Radial Glia-like → nIPC → Neuroblast) to Granule mature
  • • Template 2 (latent time): latent time gradient follows the same direction and orders cells more finely than a cluster-based pseudotime
  • • Template 3 (loom): identical output to Template 1 if using the same underlying data; barcode format is the most common source of mismatch
  • • Template 4 (STARsolo arrow): the arrow plot exposes per-cell velocity quality — coherent regions vs. noisy ones
  • • All templates: only a subset of the 2,000 top variable genes passes the fit and is flagged in adata.var["velocity_genes"] — inspect that column rather than assuming a number

Tips & Troubleshooting

Very few velocity genes (<100)

Lower min_shared_counts (try 5 or even 1) or increase n_top_genes. Also check that your data actually contains unspliced reads — some count matrices from public repositories only include spliced counts.

Arrows appear completely random

This usually means the velocity computation failed silently. Check the node log for scVelo warnings about “poor fit”. Try switching from stochastic to dynamical mode, or check that your spliced/unspliced layer names are correct (they must be exactly “spliced” and “unspliced”).

Dynamical mode is too slow

Filter your data to fewer cells (Subset Data) before running the dynamical model, or prototype with stochastic mode and switch to dynamical once the pipeline is settled. A running node can be cancelled from the execution overlay.

Going Further

  • Use velocity as input to CellRank (CellRank Kernel with kernel_type: velocity → Cell Fate Analysis → Lineage Drivers → Gene Trends) to compute fate probabilities and gene-expression trends along each lineage. See the CellRank node reference.
  • Compare velocity-based ordering with PAGA + DPT in the trajectory tutorial — latent time should align with DPT direction in well-fitted datasets. PAGA can also take directed edges from velocity (use_rna_velocity).
  • If velocity is noisy, inspect adata.var["velocity_genes"] to find which genes drove the fit, and consider raising min_shared_counts or filtering low-quality cells before the RNA Velocity node.

Glossary

Spliced / Unspliced
Two count layers per cell: spliced = mature mRNA (no introns); unspliced = nascent pre-mRNA (intron-containing). The ratio between them encodes the direction of transcriptional change.
Stochastic vs. Dynamical mode
scVelo's velocity models. Stochastic = moment-based, fast, the node default, suitable for most cases. Dynamical = full kinetic model with EM, much slower, gives latent time. Use stochastic for exploration; dynamical for publication and developmental datasets.
Latent time
A continuous per-cell ordering recovered from the dynamical model (range [0, 1]). Unlike DPT, no root cell is needed — latent time emerges from the kinetic fit.
Terminal state
An end point of the velocity transition graph — a state cells flow toward but not away from. The Terminal States node uses scVelo's eigenvector-based routine; typically corresponds to fully differentiated cell types. CellRank's Cell Fate Analysis offers the GPCCA alternative with fate probabilities.
min_shared_counts
Minimum number of cells with both spliced & unspliced reads required to keep a gene. Lower for sparse data (STARsolo, low-coverage); higher for dense data.