Jupyter (notebook embedding + hooks)#

This page documents the notebook-facing APIs:

  • Fastest notebook preview: show_anndata() (serve AnnData directly)

  • Reproducible notebook view: show() (view an exported dataset directory)

  • Interactive control: viewer objects (AnnDataViewer, CellucidViewer) support 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 cellucid

  • If you use AnnData: install anndata (and typically numpy, scipy, pandas).

  • A notebook environment: classic Jupyter, JupyterLab, VSCode notebooks, or Google Colab.


Fast path (beginner-friendly)#

A) “I have an AnnData”#

from cellucid import show_anndata

viewer = show_anndata(adata)  # or "data.h5ad" / "data.zarr"

What you should see:

  • A Cellucid viewer embedded in the notebook output, showing your embedding.

  • (Optionally) a one-time “viewer UI cache” download progress message on first run.

B) “I already exported a dataset folder”#

from cellucid import show

viewer = show("./my_export")

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:

2) Choosing show_anndata vs show#

Use show_anndata() when:

  • you’re iterating quickly in analysis notebooks,

  • you don’t want to write an export folder yet,

  • you can accept slower loads and less shareable artifacts.

Use show() when:

  • you want reproducibility (the export folder is a concrete artifact),

  • you want speed (pre-quantized, pre-packed binaries),

  • you want to share the dataset directory or host it.

3) A minimal “hooks” example (viewer → Python)#

from cellucid import show_anndata

viewer = show_anndata(adata)

@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)#

# Highlight a few cells (indices into the dataset)
viewer.highlight_cells([0, 10, 20], color="#ff0000")

# Color points by an obs field (must exist in the dataset/AnnData)
viewer.set_color_by("cell_type")

# Hide a subset (or pass None to apply to all)
viewer.set_visibility([1, 2, 3], visible=False)

# Reset the camera
viewer.reset_view()

5) Waiting for the viewer to be ready (robust notebooks)#

If you need to run code only after the UI finished loading:

viewer.wait_for_ready(timeout=30)

6) Capturing a .cellucid-session bundle without downloading (advanced)#

This is the “no browser download” workflow:

bundle = viewer.get_session_bundle(timeout=60)
print(bundle.list_chunk_ids())

See Sessions (.cellucid-session bundles) for what a session bundle contains and how to apply it back onto an AnnData.



Deep path (how notebook embedding works)#

What starts when you call show(...) / show_anndata(...)#

  1. A local HTTP server starts (on 127.0.0.1:<port>).

  2. The viewer UI is loaded from the same origin via a hosted-asset proxy (to avoid mixed-content and cross-origin issues).

  3. The viewer UI posts interaction events back to the local server at /_cellucid/events.

  4. The Python viewer routes those events to your hook callbacks (e.g., @viewer.on_selection).

Environment caveat: when the browser cannot reach the kernel’s localhost#

Some notebook setups run the kernel remotely. In those cases, your browser may not be able to reach http://127.0.0.1:<port> directly.

If that happens, set:

  • CELLUCID_CLIENT_SERVER_URL to a browser-reachable URL for the running Cellucid server (HTTPS if required).


API reference#

Functions#

show_anndata#

cellucid.show_anndata(data, height=600, **kwargs)[source]#

Quickly display an AnnData object, h5ad file, or zarr store in a notebook.

This is the easiest way to visualize AnnData directly without running prepare first. However, it’s slower than using pre-exported data.

Parameters:
  • data (str | Path | anndata.AnnData) – AnnData object, path to h5ad file, or path to zarr directory.

  • height (int) – Height of the viewer in pixels.

  • **kwargs – Additional arguments passed to AnnDataAdapter - latent_key: Key in obsm for latent space (auto-detected) - gene_id_column: Column in var for gene IDs (“index” by default) - normalize_embeddings: Normalize to [-1,1] (default True) - dataset_name: Human-readable name

Return type:

AnnDataViewer

Returns:

AnnDataViewer instance for interaction via hooks.

Supported formats:
  • In-memory AnnData objects

  • .h5ad files (HDF5-based, lazy loading via backed mode)

  • .zarr directories (directory-based, inherently lazy-loaded)

Example

>>> from cellucid import show_anndata
>>> viewer = show_anndata(adata)
>>>
>>> @viewer.on_selection
... def handle(event):
...     subset = adata[event['cells']]
...     sc.pl.violin(subset, 'gene1')
>>> # With custom options
>>> viewer = show_anndata(adata, latent_key="X_pca", height=800)

Note

For production use or sharing, consider using prepare to create optimized binary files, then use show() to display them.

show#

cellucid.show(data_dir, height=600)[source]#

Quick function to display a cellucid dataset in a notebook.

Parameters:
  • data_dir (str | Path) – Path to the dataset directory

  • height (int) – Height of the viewer in pixels

Return type:

CellucidViewer

Returns:

CellucidViewer instance for interaction via hooks.

Example

>>> from cellucid.jupyter import show
>>> viewer = show("/path/to/my_dataset")
>>>
>>> @viewer.on_selection
... def handle(event):
...     print(f"Selected {len(event['cells'])} cells")

Classes (see dedicated page)#


Edge cases (do not skip)#

Data size and performance#

  • Very large adata.X can be slow in AnnData mode; consider exporting with prepare().

  • If your .h5ad is huge, prefer serving in backed/lazy mode (default) rather than loading fully into RAM.

“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#


Troubleshooting (symptom → diagnosis → fix)#

Symptom: “Viewer does not appear in the notebook output”#

Likely causes:

  • You are not actually running inside a notebook (e.g., plain Python script).

  • The notebook output is blocked (content security policy / iframe blocked).

How to confirm:

  • Print viewer.viewer_url and try opening it in a new browser tab.

Fix:

  • Use a notebook environment (Jupyter, JupyterLab, VSCode notebooks, Colab).

  • If iframes are blocked, open the printed URL manually.


Symptom: “Notebook proxy required” / the iframe shows a proxy warning#

Likely causes:

  • The browser cannot reach the kernel’s localhost server (remote kernel or strict origin policy).

Fix options:

  • Recommended: install/enable jupyter-server-proxy (environment dependent).

  • Or set CELLUCID_CLIENT_SERVER_URL to a browser-reachable URL for the server.


Symptom: “Port already in use”#

Fix:

  • Choose a different port (pass port=... to the viewer class, or stop the process using the port).


Symptom: “I registered @viewer.on_selection but nothing prints”#

Likely causes:

  • The viewer is not ready yet.

  • You selected nothing (or selection tools differ from expectation).

How to confirm:

  • Call viewer.wait_for_ready() and then try selecting again.

Fix:

  • Ensure the viewer is loaded, then select cells (lasso/shift-click/etc.).


Symptom: “The iframe is blank / white / stuck loading”#

Likely causes:

  • The viewer UI failed to load (offline, blocked network, or cache missing).

  • The server isn’t reachable from the browser (remote kernel / HTTPS notebook constraints).

How to confirm:

  • Open viewer.viewer_url in a new browser tab (sometimes notebook output hides useful errors).

Fix:

  • If you are offline, run once while online (to populate the viewer UI cache), then retry.

  • If the browser cannot reach localhost, set CELLUCID_CLIENT_SERVER_URL (or use a notebook proxy).


Symptom: “Cellucid: Viewer UI unavailable” page#

Likely causes:

  • The hosted viewer assets could not be fetched and the local cache is empty/stale.

How to confirm:

  • Run viewer.debug_connection() and inspect the web_ui section.

Fix:

  • Ensure the runtime can reach the hosted viewer assets once (HTTPS).

  • Set CELLUCID_WEB_PROXY_CACHE_DIR to a persistent, writable directory.


Symptom: “viewer.set_color_by(...) does nothing”#

Likely causes:

  • The field name does not exist in obs (or has a different spelling/case).

  • The viewer is not ready yet.

How to confirm:

  • Wait for readiness: viewer.wait_for_ready().

  • In AnnData mode, print adata.obs.columns and confirm the field exists.

Fix:

  • Use the exact field name.

  • Prefer exporting with prepare() for stable field naming across sessions.


Symptom: “viewer.highlight_cells(...) does nothing”#

Likely causes:

  • The viewer is not ready yet.

  • Indices are out of range (wrong dataset / wrong cell order).

How to confirm:

  • viewer.wait_for_ready()

  • Try highlighting a small index like [0].

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 adata after starting the viewer; restart the viewer after reindexing.


Symptom: “My hook callback crashes silently”#

What’s happening:

  • Hook callbacks are wrapped; exceptions are logged rather than crashing the notebook cell.

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_message to inspect raw event payloads.


Symptom: “I don’t know what’s wrong / I need a diagnostic dump”#

Fix:

  • Run:

    report = viewer.debug_connection()
    report
    
  • The report includes:

    • server health/info probes,

    • detected notebook context (Jupyter/Colab/VSCode),

    • viewer UI cache status,

    • recent events and readiness state.


See also#