Skip to contents

Cellucid logo

R-CMD-check Documentation Status License: BSD-3-Clause

View and prepare single-cell data in R for Cellucid — interactive, GPU-accelerated visualization in the browser.

cellucid (repo: cellucid-r) opens a Seurat, SingleCellExperiment or SummarizedExperiment object in the Cellucid viewer from R — in the RStudio or Positron Viewer pane or a browser, with no Python — and writes it (or your embeddings, metadata, gene expression, neighbour graph and vector fields) as a Cellucid-ready AnnData: a standard AnnData Zarr store that the Cellucid web app opens directly and Python’s anndata reads like any other. Several assays become a Cellucid-ready MuData.

Active package version — 0.9.7

Version 0.9.7 is the active Cellucid for R source and documentation version, and it is the CRAN submission release. The CRAN package index is authoritative for registry availability; the Installation guide below gives both exact installation paths.

Highlights

  • View an object from R: cellucid_view(seu) serves the object from memory as the store cellucid_prepare() would write, uncompressed over loopback and making only what the viewer reads (a million-cell object’s first gene in about a third of a second, never transposing its matrix), so a 100,000-cell object opens in under a second; do anything a person can do in the viewer from R – every action of its catalogue, cellucid_act(viewer, "color.by", ...) or its own function, cellucid_color_by(), cellucid_highlight_cells(), cellucid_history_get(), … – each one step of the viewer’s history, labelled as R’s; listen with cellucid_on("selection", ...); ask for its session (its whole history) with cellucid_request_session() (then cellucid_session_result()), read it with cellucid_read_session() – trackers, branches, groups, steps, notes and turns as data frames (cellucid_steps(), cellucid_groups(), cellucid_turns(), …), the methods paragraph and pseudocode of a branch – and apply any step or branch back onto the object with cellucid_apply_session() (a plan first, then cellucid_apply_plan(); each apply recorded); group R’s actions into one turn of the history with cellucid_turn(), hear every change with cellucid_on("history_changed", ...) and push a session file into the viewer with cellucid_load_session(); write everything made in it (highlights, labels, scores, gene sets) back onto the object in one call with cellucid_pull(seu, viewer) (or from the web app’s exports, cellucid_pull(seu, "exports/")), and send a column to it with cellucid_push() (a gene set with cellucid_gene_set_save()); cellucid_serve() for a cluster behind an SSH tunnel; in a Jupyter notebook (IRkernel) the viewer is the cell’s output, the widget cellucid-python shows, with no port
  • Objects in, directly: cellucid_prepare(data = seu) reads Seurat (v3–v5), SingleCellExperiment and SummarizedExperiment objects as the web app reads them; any argument given replaces that component
  • One standard format: an AnnData Zarr store (format 2, or 3 with zarr_format = 3) with consolidated metadata — a .zarr folder for GitHub (format = "zarr"), or one .zarr.zip file (format = "zarr.zip") for everywhere else: single-file hosts (Zenodo, Figshare, Hugging Face, S3, a lab server), local files, cellucid serve and Jupyter
  • Fast in the browser: gene-major expression (CSC), chunked for range reads, byte-shuffled where that helps and compressed with deflate, which browsers decompress natively; a .zarr.zip keeps its metadata at the end of the archive, so it opens with one read
  • Subsamples read only their cells: the cells are stored (and served by cellucid_view()) in priority order (priority_seed, default 0), so a share the viewer opens is the first rows of every array and the first values of every gene; cellucid_subsample_cells() gives exactly the cells the viewer and Python’s cellucid.subsample_cells() keep, and cellucid_restore_original_order() puts an object read from such a file back in the order its cells were given
  • Self-describing: the dataset’s id, name, citation, source, and launch defaults live in uns['cellucid'], with each categorical obs column’s outlier quantiles in the latent space named by latent_key, so the viewer’s outlier filter works as soon as the file opens
  • Light dependencies: jsonlite, and httpuv, later and curl for the viewer (optional: Matrix, SeuratObject, SingleCellExperiment / SummarizedExperiment, each needed only for its own objects; IRkernel and IRdisplay for the viewer in a Jupyter notebook)
  • Flexible inputs: R objects, or raw matrices and data frames

Every value is stored as float32 (embeddings, expression, vector fields, the graph; expression as float64 only for integer counts above 2^24, which float32 cannot hold exactly) and must be finite. A neighbour graph must be exactly symmetric, with a zero diagonal and non-negative weights; a stored zero in a sparse input is not an edge and is dropped.

gene_expression is always interpreted as cells × genes. Cellucid validates its row and column counts but never guesses or transposes orientation; for a square matrix, shape alone cannot reveal a genes × cells input.

Installation

Use the dedicated Installation guide to choose CRAN or GitHub based on current registry availability. To install the active source directly from the official GitHub repository:

install.packages("remotes")
remotes::install_github("theislab/cellucid-r")

Source installation compiles a few small C routines (checksums, the outlier quantiles, one-pass matrix checks, the decoders of a saved session, linked against zlib) and therefore needs Rtools on Windows, the Xcode Command Line Tools on macOS, or a C toolchain plus the R and zlib development headers on Linux. A platform CRAN binary, when available, does not require a local compiler.

Optional but recommended (sparse expression matrices and neighbour graphs):

See the dedicated Installation guide for requirements, verification, upgrades, and the CRAN availability status.

Quickstart

library(cellucid)

# View an object
viewer <- cellucid_view(seu)                 # a Seurat, SingleCellExperiment or SummarizedExperiment
cellucid_color_by(viewer, field = list(source = "obs", key = "seurat_clusters"))
cellucid_selection(viewer)                   # the cells you selected in the viewer
seu <- cellucid_pull(seu, viewer)            # every highlight, label and score made in the viewer, as meta.data columns
cellucid_push(viewer, seu$seurat_clusters, key = "seurat_clusters")  # a column of yours, into the viewer
cellucid_stop(viewer)

# Write it for sharing
cellucid_prepare("pbmc.zarr", "zarr", data = seu, dataset_id = "pbmc", dataset_name = "PBMC")

# Or write components you hold as matrices

cellucid_prepare(
  out_path = "my_dataset.zarr",       # or "my_dataset.zarr.zip" with format = "zarr.zip"
  format = "zarr",
  embeddings = list(                  # obsm: any number, every column kept
    X_umap = umap,
    X_pca = pca
  ),
  dataset_id = "my-dataset",
  dataset_name = "My dataset",
  obs = obs,                          # data.frame, one row per cell
  var = var,                          # data.frame, one row per gene
  gene_expression = expr,             # cells × genes, sparse or dense
  connectivities = knn,               # optional cells × cells graph
  embedding_defaults = c("2d" = "X_umap"),
  latent_key = "X_pca"
)

In a Jupyter notebook show the viewer in one cell and call cellucid_pull() in the next: R hears the viewer between cells. Seven runnable notebooks, each with a Python twin and an “Open in Colab” badge, walk through the round trip: Notebook tutorials.

Open a .zarr.zip file from your computer in the web app at https://www.cellucid.com, or a .zarr folder or .zarr.zip file published at a web address with the Link or server address bar; read either in Python with anndata.read_zarr() (its cells in priority order: cellucid.restore_original_order() in Python, or cellucid_restore_original_order() on what anndataR or zellkonverter read, gives them back in the order you passed them).

The store is written beside out_path under a temporary name and moved into place only when it is complete; force = TRUE replaces an existing store. Vector fields are stored under scVelo’s names and drawn over the embedding they are named for: vector_fields = list(velocity = list(X_umap = v)) stores obsm['velocity_umap'], and X_branches = vb stores obsm['velocity_branches'], both listed by the viewer as the field velocity. cellucid_transition_drift() computes such a field from a cell-cell transition matrix (CellRank’s, for example), the computation of Python’s compute_transition_drift(). Repeated cell or gene names are made unique the way anndata does it (MATR3, MATR3-1), and var_gene_id_column names the var column, such as gene symbols beside Ensembl row names, that the viewer shows.

Agent apps on your computer

cellucid_mcp() is a local MCP server that lets an agent app on your computer (Claude Code, Claude Desktop, Cursor) work in a Cellucid viewer, with the web app’s own tools under your permission settings: every change is a step of your history, asked in the viewer’s confirm card (and in the app) when your settings say so, and one Undo turn takes the agent’s turn back. Register it once:

claude mcp add cellucid -- Rscript -e "cellucid::cellucid_mcp()"

It drives the newest viewer open on this computer (cellucid_view() or cellucid_serve() in R, show_anndata() in Python); viewer = names one, and open = "data.zarr" serves a file in a viewer of its own. A notebook widget’s viewer (IRkernel) has no server and is not reached; CELLUCID_AGENTS=off publishes no viewer to agents at all. A dataset’s “Agents see names and summaries only” switch, set in the viewer, applies to these agents too. See ?cellucid_mcp.

Citation

Placeholder citation. The Cellucid preprint is in preparation for bioRxiv and has no DOI yet; the reference below is a placeholder and will be replaced when the preprint is published.

If you use Cellucid, please cite:

Inecik, K. PLACEHOLDER: Cellucid preprint, title to be announced (forthcoming on bioRxiv). bioRxiv (in preparation).

BibTeX and other formats: Citing Cellucid.

Community

License

BSD-3-Clause