Conversion · Loom → Seurat object (RDS)
How to Convert Loom to Seurat object (Without Losing Your Metadata)
SeuratDisk's Connect-then-as.Seurat path works, but velocyto barcodes and Seurat v5 layers will quietly wreck your merge if you skip the cleanup step.
By Ming "Tommy" Tang, Director of Bioinformatics in Big Pharma · Reviewed September 2026 · 2 min read
- Loom
- .loom · coordinates: n/a
- Seurat object (RDS)
- .rds, .RDS · coordinates: n/a
You hit this conversion when you've run velocyto (or an equivalent pipeline) and need its spliced/unspliced loom output inside a Seurat workflow for RNA velocity, or when you've downloaded a public loom dataset and want it in R instead of Python's scanpy/anndata stack. LoomR, the original R loom reader, is abandoned. The only maintained path right now is SeuratDisk's Connect() followed by as.Seurat().
What changes is the shape, not just the file extension. Loom's row_attrs and col_attrs are flat HDF5 tables with no concept of "assay" or "layer," so as.Seurat() has to decide which loom matrix becomes counts, which becomes data, and which attributes become meta.data columns. Seurat v5 makes this worse: counts/data/scale.data are now named layers on one assay instead of fixed slots, and SeuratDisk, built around the older slot model, doesn't reliably know where to put loom's spliced/unspliced matrices in that structure. Check your Seurat and SeuratDisk versions before trusting the mapping.
The failure that never throws an error: velocyto loom barcodes carry sample or lane identifiers that don't match the barcodes in a Seurat object you built directly from Cell Ranger output. Merge the two without reconciling those strings first and you don't get an error, you get a merge that silently keeps almost no overlapping cells, or duplicates cells under slightly different names. Check the barcode intersection between objects before you merge, not after.
The commands
Type your file names once; every command below updates.
01SeuratDiskvSeuratDisk >= 0.0.0.9021 recommended for Seurat v5 compatibility
rlibrary(SeuratDisk) loom_conn <- Connect(filename = "sample.loom", mode = "r") seurat_obj <- as.Seurat(loom_conn)Connect() opens the loom's HDF5 file read-only without loading everything into memory at once. as.Seurat() then walks its row/col attributes and matrices to build an assay plus meta.data and meta.features. Use this for a generic loom file, not one produced by velocyto.
02SeuratDisk
rlibrary(SeuratDisk) velocity_data <- ReadVelocity(file = "sample.loom") seurat_obj <- as.Seurat(velocity_data)ReadVelocity() is the velocyto-aware reader: it expects the loom to carry separate spliced and unspliced matrices and returns them as named layers/assays instead of collapsing them into one counts matrix. Use this instead of Connect()+as.Seurat() whenever the loom came out of velocyto or velocyto.py.
03base R
rsaveRDS(seurat_obj, file = "sample.rds")Serializes the in-memory Seurat object built by the previous step to a single .rds file on disk. as.Seurat() only produces the R object in memory; this is the step that actually writes it to disk, and skipping it is the most common reason the conversion looks like it failed.
Coordinates, strand, names, builds
Neither loom nor Seurat's RDS encode genomic coordinates or strand; both are single-cell matrix formats, so 0-based versus 1-based and chr-naming don't apply here. What does apply: loom's row_attrs (gene-level) and col_attrs (cell-level) are flat tables with no fixed schema, so names like Gene or CellID may or may not land as meta.features and meta.data columns with the labels you expect. Check colnames(seurat_obj@meta.data) and rownames(seurat_obj) after conversion instead of assuming. Loom global attributes stored at the file level rather than per-row or per-cell have no Seurat slot to receive them and are dropped silently. Velocyto's spliced/unspliced matrices become separate assays or layers depending on your Seurat/SeuratDisk version combination, and cell barcodes may carry sample-prefix or lane-suffix formatting from the loom pipeline that won't match barcodes from a directly-imported Cell Ranger matrix, breaking merges with no warning at all.
Check the output before you trust it
01Dimensions match the loom file
rdim(seurat_obj)Expected Matches the loom's original (n_genes, n_cells); a mismatch means as.Seurat() dropped rows or columns during conversion.
02Barcodes are real, not placeholders
rhead(colnames(seurat_obj))Expected Original cell barcode strings (e.g. AAACCTGAGAAACCAT-1), not generic labels like Cell1, Cell2.
03Expected assays are present
rAssays(seurat_obj)Expected spliced and unspliced for velocyto loom, or the assay name you expect; a single generic RNA assay means the layers got collapsed.
04Metadata columns survived
rcolnames(seurat_obj@meta.data)Expected Includes the loom's original col_attrs names in addition to Seurat defaults (orig.ident, nCount_RNA, nFeature_RNA); only defaults means cell metadata didn't transfer.
05Raw counts are integers
rGetAssayData(seurat_obj, layer = "counts")[1:5, 1:5]Expected Whole numbers; non-integer values mean normalized data got mapped into the counts layer by mistake.
06Barcode overlap before merging
rlength(intersect(colnames(seurat_obj), colnames(existing_obj)))Expected Close to the number of shared cells you expect; near-zero overlap means barcode formatting differs between the two objects and needs cleanup before merge.
Errors you will see, and what they mean
- Error: 'slot' is deprecated in favor of 'layer'
- Cause: SeuratDisk was written against Seurat's pre-v5 slot-based Assay object; when Seurat v5's layer-based Assay5 class is loaded, functions that still pass the old slot parameter break. Fix: Pin Seurat and SeuratDisk to versions built for the same Assay class generation, or convert the loom in a Seurat v4-compatible environment before upgrading the object to v5.
- R session runs out of memory or crashes during as.Seurat() on a large loom
- Cause: as.Seurat() materializes the full matrix into memory rather than streaming it, so a loom file approaching or exceeding 1M cells can exceed available RAM. Fix: Subset the loom file to the cells or genes you need via the loom connection's indexing before calling as.Seurat(), or convert and process one sample at a time.
- Merged object has far fewer cells than expected, with no error thrown
- Cause: Velocyto loom barcodes carry sample or lane identifiers formatted differently from the barcodes in a Seurat object built directly from Cell Ranger output, so colnames() don't match during merge. Fix: Inspect and reformat barcode strings with regex on both objects' colnames() so they match exactly before calling merge() or AddMetaData().
- Duplicate cell names not allowed when combining spliced and unspliced assays
- Cause: Barcodes from the loom-derived object aren't uniquely suffixed against the cells already present in your working Seurat object. Fix: Call RenameCells() on one of the objects to add a distinguishing prefix or suffix before merging.
- there is no package called 'loomR'
- Cause: LoomR was the original R loom reader but is unmaintained and no longer available from its install source, so scripts and tutorials that still reference it fail on a fresh R installation. Fix: Replace loomR calls with SeuratDisk's Connect() and as.Seurat(), which is the maintained path.
Questions people ask
- Can I convert a .loom file straight to a .rds file in one step?
No single function does both. You connect to the loom with
SeuratDisk::Connect(), build the in-memory object withas.Seurat(), and only then callsaveRDS()to write the .rds file. Skipping that last call is the most common reason people think the conversion 'didn't work' when really the R object exists but nothing was written to disk.- Why does SeuratDisk throw errors about 'slot' being deprecated?
That's a Seurat v5 layer-versus-slot mismatch. SeuratDisk was built around the older Assay class where counts/data/scale.data are fixed slots, while Seurat v5's Assay5 stores them as named layers instead. Match your SeuratDisk and Seurat versions to the same Assay generation, or convert the loom before upgrading the resulting object to v5.
- Is LoomR still a valid way to read loom files in R?
No. LoomR is abandoned and unmaintained. Use SeuratDisk's
Connect()plusas.Seurat()instead, which is the currently supported path for loom-to-Seurat conversion.- How do I merge velocyto spliced/unspliced data into my existing Seurat object?
Check barcode formatting first. Velocyto loom barcodes often carry sample or lane identifiers that don't match the
colnames()of a Seurat object built directly from Cell Ranger output, so a merge can silently drop almost all cells. Clean and align the barcode strings on both objects before callingmerge().- What happens to loom's metadata during conversion?
Cell-level attributes (
col_attrs) generally land inseurat_obj@meta.dataand gene-level attributes (row_attrs) inmeta.features, but the names aren't guaranteed to match what you expect, and anything stored as a loom-level global attribute has no Seurat slot to receive it and gets dropped. Always checkcolnames(seurat_obj@meta.data)right after conversion.
Related pages
- Convert · How to Convert h5ad to Seurat object (Without Losing Your Metadata)
- Convert · How to Convert Seurat object to h5ad (Without Losing Your Metadata)
- Convert · How to Convert Seurat object to SingleCellExperiment (Without Losing Your Metadata)
- Convert · How to Convert Seurat object to CSV/TSV count table (Without Losing Your Metadata)
- Convert · How to Convert Seurat object to h5Seurat (Without Losing Your Metadata)
Related reading on the blog
Sources
- LoadLoom: Loom-file Loading in mojaveazure/seurat-disk — Documents Connect() and as.Seurat() as the standard loom-to-Seurat conversion functions
- Seurat v5 Essential Commands • Seurat — Explains the v5 layer model (counts/data/scale.data) that SeuratDisk conversions have to map into
- GitHub: mojaveazure/seurat-disk, Interfaces for HDF5-Based Single Cell File Formats — Source for reported SeuratDisk compatibility issues with Seurat v5 layers and assay versions
- Interoperability between single-cell object formats • Seurat (v4.3) — Official Seurat vignette covering conversion between loom and other single-cell object formats
- loom file format, Linnarsson lab — Background on the loom format and LoomR's status as an abandoned reader