Jupyter (notebook embedding + hooks)#
This page documents the notebook-facing APIs:
Notebook view:
show_anndata()(an AnnData or MuData object, or any file or folder Cellucid reads:.h5ad, a Zarr store,.h5mu,.loom, a 10x.h5orouts/folder, a MatrixMarket folder, an.rds/.rda, or a file written byprepare())Interactive control: the widget it returns (
Widget) supports hooks + Python→frontend commands
Audience + prerequisites#
Audience
Wet lab / beginner: copy/paste the “Fast path” and use the troubleshooting section when something looks wrong.
Computational: use “Practical path” + “Edge cases” to avoid performance and data-shape pitfalls.
Power user: use “Deep path” to understand how the server + hooks + session capture work.
Prerequisites
pip install cellucidIf you use AnnData: install
anndata(and typicallynumpy,scipy,pandas).A notebook that shows widgets: JupyterLab, Jupyter Notebook 7, VS Code, Google Colab, marimo or Databricks (Supported environments matrix).
Fast path (beginner-friendly)#
A) “I have an AnnData”#
from cellucid import show_anndata
viewer = show_anndata(
adata,
dataset_name="My study",
dataset_id="my-study-v1",
)
What you should see:
A Cellucid viewer in the notebook output, showing your embedding, once the cell has finished (the viewer reads the data through the kernel between cells).
B) “I have a file written by prepare()”#
from cellucid import show_anndata
viewer = show_anndata("my_study.h5ad") # it names itself (uns['cellucid'])
Practical path (step-by-step + common workflows)#
1) Notebook display vs browser tab#
Cellucid always runs in a browser context (the viewer UI), but you choose where it appears:
Notebook output:
show_anndata()(this page), a widget.Browser tab:
AnnDataViewer(the same API), orserve_anndata()/cellucid serve …(see Server (browser tab + local HTTP server)).
2) Opening what you have, or a prepared file#
Open the object or file you have when:
you’re iterating quickly in analysis notebooks,
you want the viewer to read the
.h5ador Zarr store you already have, in place.
Open a file written by prepare() when:
you want reproducibility (the file is a concrete, self-naming artifact),
you want speed (a gene-major matrix: every gene is one small read),
you want to share the dataset or host it.
3) A minimal “hooks” example (viewer → Python)#
from cellucid import show_anndata
viewer = show_anndata(
adata,
dataset_name="My study",
dataset_id="my-study-v1",
)
@viewer.on_selection
def on_selection(event):
# event["cells"] is a list[int] of selected indices (row indices into adata)
print("Selected", len(event.get("cells", [])), "cells")
4) A minimal “control” example (Python → viewer)#
Every action of the viewer is a method; each returns an ActionRequest
whose result() (in a later cell) is the viewer’s answer:
# Highlight a few cells (rows of obs, a boolean mask, or obs names)
viewer.highlight_cells(cells=[0, 10, 20], color="#ff0000")
# Color points by an obs field (must exist in the dataset/AnnData)
viewer.color_by(field={"source": "obs", "key": "cell_type"})
# Hide a subset (or pass None for every cell)
viewer.cells_visibility_set(cells=[1, 2, 3], visible=False)
# Reset the camera of the live view
viewer.camera_reset(view="live")
# Read: the fields the viewer has (request.result() in a later cell)
request = viewer.fields_list()
5) Running code once the viewer is ready (robust notebooks)#
The viewer loads after the cell that showed it finishes, so register a hook:
@viewer.on_ready
def ready(event):
print("Viewer ready:", event)
or read viewer.state.ready in a later cell. (viewer.wait_for_ready()
returns at once when it is ready, and raises in a cell that would have to
wait: the viewer is heard only between cells.)
6) Capturing a .cellucid-session without downloading (advanced)#
This is the “no browser download” workflow, in two cells: ask, then collect.
request = viewer.request_session()
session = request.result()
print(session.tracker().steps[["id", "label", "origin"]])
plan = session.apply(adata) # what applying the step shown would change
See Sessions (.cellucid-session files) for what a session contains and how to apply it back
onto an AnnData. viewer.load_session(path) goes the other way: it adds a
saved session’s trackers to the viewer’s history.
Deep path (how notebook embedding works)#
What happens when you call show_anndata(...)#
The dataset and the launch choices are checked; an in-memory AnnData is planned as a store in priority order, made as the viewer reads it (a MuData as a MuData store), its expression matrices laid out gene-major in the background. Nothing binds a port.
A
Widgetis shown. Its output frames the Cellucid app, which your browser loads fromweb_source_url(https://www.cellucid.comby default).The app asks the widget for its credentials and a private channel, reads the dataset through it (the kernel answers each request from the dataset) and opens the viewer channel through it: commands go to the viewer and interaction events come back.
The widget routes those events to your hook callbacks (e.g.,
@viewer.on_selection), between cells.
Architecture: Architecture: the viewer channel.
API reference#
Functions#
show_anndata#
- cellucid.show_anndata(data, height=600, *, latent_key=None, gene_id_column=None, normalize_embeddings=None, centroid_min_points=None, dataset_name=None, dataset_id=None, obs_keys=None, embedding_keys=None, knn_key=None, expression_source=None, vector_field_default=None, modality=None, web_source_url='https://www.cellucid.com', cache_bytes=None)[source]#
Show an AnnData or MuData object, or any file Cellucid reads, in a notebook.
Returns a
cellucid.jupyter.Widgetand shows it in the cell’s output: in JupyterLab, Jupyter Notebook 7, VS Code, Google Colab and Databricks it is displayed at once; in marimo, make it the cell’s output (the last expression). The widget reaches this kernel through the notebook’s own connection, so no port, proxy or server address is involved. The viewer reads the data itself, without running prepare first: in place with range requests where the format allows it, once from the start for an R object or a MatrixMarket folder. An in-memory AnnData, and an AnnData, MuData, Loom or 10x file not in priority order, are served as a Zarr store with their cells in priority order, made as the viewer reads it, so a subsample reads only its cells; so is a MuData object. An object’s expression matrices are laid out gene-major in the background (read-only meanwhile), so the viewer opens at once; a cell of the notebook run again stops the widget it showed before.To open a viewer in a browser tab of its own instead (a script, an SSH tunnel), use
AnnDataViewer, orserve_anndata/cellucid serve.- Parameters:
data (str | Path | anndata.AnnData | mudata.MuData) – AnnData or MuData object, or path to a file or folder Cellucid reads (see “Supported formats”). A leading
~in a path is expanded to the user’s home directory.height (int) – Height of the viewer in pixels.
latent_key (str | None) –
obsmarray (a latent space such as scVI or PCA) the viewer’s outlier filter measures in, instead of the data’s ownuns['cellucid']['defaults']['latentKey']. The viewer reads the outlier quantiles the data stores for it, or computes them on first use.gene_id_column (str | None) – Exact column in
var(raw.varwithexpression_source="raw") whose values name the genes. If None, genes are named by the index.normalize_embeddings (bool | None) – Whether the viewer scales embeddings into its coordinate range.
centroid_min_points (int | None) – The fewest cells a category needs to get outlier quantiles (10 when None).
dataset_name (str | None) – Exact non-empty human-readable name without surrounding whitespace or control characters. Defaults to
uns['cellucid']['name'], else the file’s name, orAnnData (<n> cells)for an object.dataset_id (str | None) – Portable 1-180 byte ASCII identifier beginning with a letter or digit and otherwise using letters, digits,
.,_, or-; no trailing.or Windows device name. Defaults touns['cellucid']['id'], else made from the file’s name without its extension, or from the object’s label (AnnData (N cells)) (said in one line: sessions are kept under it).obs_keys (Sequence[str] | None) – Exact
obscolumns the viewer shows, in this order. If None, every column is shown.embedding_keys (Mapping[str, EmbeddingKeySpec] | None) – The embedding of a dimension slot, such as
{"2d": "X_tsne"}or{"2d": ("X_pca", [2, 0])}with 0-based columns. Slots not named follow the default rule, and a deterministic best guess applies when no slot resolves.knn_key (str | None) – The
obspmatrix the viewer draws as the neighbour graph, such as"connectivities"; read only when a view draws it. None follows the viewer’s default rule.expression_source (str | None) –
"X","raw", or"layer:<name>"; None follows the viewer’s default rule (rawwhen it holds more genes thanXorXis scaled, as Scanpy reads genes; elseX).vector_field_default (str | None) – The vector field the overlay opens on:
<field>of an obsm array<field>_<basis>drawn overX_<basis>(velocityforvelocity_umap).modality (str | None) – The modality a multimodal dataset (a MuData) opens on; None opens the default one. Set only for multimodal data.
web_source_url (str) – Where the browser loads the Cellucid app the widget shows:
https://www.cellucid.com(the default), or the address of a copy you host. The browser, not the kernel, loads it.cache_bytes (int | None) – What the kernel keeps of what it makes for a store served in priority order, in bytes (least recently used first out); None keeps a 64th of the machine’s memory, 0 nothing (as
cellucid.serve_anndata()’s).
- Return type:
Widget
- Returns:
The
cellucid.jupyter.Widget, for hooks and commands.
- Supported formats:
.h5ad, AnnData Zarr (folder or .zip), .h5mu, MuData Zarr, .loom, 10x .h5 files and Cell Ranger outs/ folders: a file prepare() wrote read in place, any other served in priority order as the viewer reads it
MatrixMarket folders (a .mtx[.gz] file of any name, such as GEO’s GSM…_matrix.mtx.gz) and R objects (.rds, .rda, .RData, gzipped or not), read once
In-memory AnnData and MuData objects, served in priority order as the viewer reads them, their expression matrices laid out gene-major in the background
Example
>>> from cellucid import show_anndata >>> viewer = show_anndata( ... adata, ... dataset_name="Example", ... dataset_id="example", ... ) >>> >>> @viewer.on_selection ... def handle(event): ... subset = adata[event['cells']] ... sc.pl.violin(subset, 'gene1')
Note
To publish a dataset for others, prepare it (or publish the .h5ad or Zarr store itself) and share a link; see the data loading guide.
Classes (see dedicated page)#
WidgetandAnnDataViewerare documented on Viewer classes (Jupyter objects you control from Python).
Edge cases (do not skip)#
Data size and performance#
A path is never loaded into the kernel’s memory: the viewer reads an
.h5ad, a Zarr store, an.h5mu, a.loomor a 10x file in place with HTTP range requests (only what a view needs), and an R object or a MatrixMarket folder once from the start.An in-memory AnnData is served as a Zarr store with its cells in priority order, each chunk made from the object when the viewer asks for it, so a subsample transfers only its cells; a cell-major sparse
Xis laid out gene-major once, in the background, into memory-mapped files removed when the viewer stops or Python exits. A MuData is written to a temporary Cellucid-ready.h5muwhen the viewer starts.
“My embedding is not 2D/3D”#
Cellucid supports 1D/2D/3D embeddings; if the embedding is missing or has an unexpected shape, you’ll see missing plot / errors.
Duplicate gene identifiers#
The viewer makes repeated gene names unique (
MATR3,MATR3-1), so every gene stays reachable; name genes by anothervarcolumn withgene_id_columnwhen the index holds identifiers you do not search by.
Troubleshooting (symptom → diagnosis → fix)#
Symptom: “Viewer does not appear in the notebook output”#
Likely causes:
The notebook cannot show widgets: the output shows text (“This output is a Cellucid viewer (a notebook widget)…”) instead.
You are not in a notebook (a plain script):
show_anndata()raises and names the browser-tab alternatives.
Fix:
Install
anywidgetwhere the notebook server runs (pip install anywidget) and reload the page, or use a notebook that shows widgets (Supported environments matrix).Outside a notebook use
AnnDataViewerorcellucid serve.
Symptom: “The output says the page has not answered as a Cellucid viewer”#
Likely causes:
The browser cannot load the app from
web_source_url(offline, a blocked network), or that address is not a Cellucid build that can be embedded.
Fix:
Check that
https://www.cellucid.comopens in the same browser, or host a copy and passweb_source_url=.
Symptom: “The output says it is waiting for the notebook kernel”#
What’s happening:
A cell is still running; the kernel answers the viewer between cells.
Fix:
Let the cell finish (or interrupt it); the viewer connects at once.
Symptom: “wait_for_ready() / request.result() raised at once”#
What’s happening:
A notebook widget hears the viewer only between cells, so a call that waits for the viewer cannot be answered in the cell that waits; the message says what to do instead.
Fix:
Use a hook or
viewer.statein a later cell; for an action’s answer or a session, keep the request (request = viewer.request_session()) in one cell and callrequest.result()in the next.
Symptom: “I registered @viewer.on_selection but nothing prints”#
Likely causes:
The viewer is not ready yet (it loads once the cell that showed it ends).
You selected nothing (or selection tools differ from expectation).
A cell is still running: hooks run when the kernel is idle.
How to confirm:
In a new cell, check
viewer.state.ready.
Fix:
Ensure the viewer is loaded, then select cells (lasso/shift-click/etc.).
Symptom: “viewer.color_by(...) does nothing”#
Likely causes:
The field name does not exist in
obs(or has a different spelling/case).
How to confirm:
Collect the request:
request.result()(in a later cell) raisesActionErrorwith the viewer’s reason, which lists fields like the one named.Register
@viewer.on_command_error: a refused action reports why.
Fix:
Use the exact field name.
Prefer a file written by
prepare()for stable field naming across sessions.
Symptom: “viewer.highlight_cells(...) does nothing”#
Likely causes:
Rows are out of range (wrong dataset / wrong cell order): Python refuses a row beyond the dataset before sending.
How to confirm:
Try highlighting a small set like
cells=[0].Collect the request (
request.result()) or register@viewer.on_command_error: a refused action reports why.
Fix:
Ensure indices correspond to the dataset loaded in the viewer.
If you subset/reorder AnnData, re-run the viewer from the same object.
Symptom: “Selections map to the wrong cells in my AnnData”#
Likely causes:
The viewer is connected to a dataset whose row order does not match your
adata.
How to confirm:
Select a single cell and print identifying metadata from
adata.obs.iloc[cell_index].Compare with what you expect in the UI.
Fix:
Ensure you apply selection indices to the same AnnData used to create/serve the viewer.
Avoid reordering
adataafter starting the viewer; restart the viewer after reindexing.
Symptom: “My hook callback crashes silently”#
What’s happening:
Hooks run on the viewer channel’s thread, not in your cell. An exception a hook raises is logged with its traceback (logger
cellucid.viewer_channel, “A viewer event hook failed”), never raised in the notebook. The event is still recorded inviewer.stateand handed towait_for_event(it is recorded before any hook runs), and the other hooks still run for it. The channel stays open.
How to confirm:
Enable debug logging:
import logging logging.basicConfig(level=logging.DEBUG)
Fix:
Add defensive checks inside callbacks (validate keys, lengths, types).
Start with
@viewer.on_messageto inspect raw event payloads.
Symptom: “I don’t know what’s wrong / I need a diagnostic dump”#
Fix:
Run:
report = viewer.debug_connection() report
For a notebook widget the report includes where the app is loaded from, the connected outputs, whether the channel is open, the readiness state and recent events (it waits for nothing). For a browser-tab
AnnDataViewerit also probes the server (health, info, every listed store), the web build cache, and pings the viewer over the channel.
See also#
Server (browser tab + local HTTP server) for browser-tab workflows and SSH tunneling
Data preparation (prepare) for Cellucid-ready AnnData files (
prepare(...))Sessions (.cellucid-session files) for
.cellucid-sessionfiles (capture + apply)