Publish a Custom Dataset Repository#
A public Cellucid dataset repository is useful when you want a colleague,
reviewer, class, or workshop participant to open your datasets without
installing Python or waiting for you to keep a server running. The repository
is ordinary static content: a dataset catalog, one datasets.json beside
the files it lists.
The small
cellucid-demo-custom-datasets
repository is the reference implementation. It contains a catalog of five
deterministic synthetic datasets: three AnnData Zarr folders written by
cellucid.prepare(..., format="zarr"), a CITE-seq MuData Zarr folder written by
cellucid.prepare(data=mdata, format="zarr"), and the same CITE-seq cells as a
Seurat object saved as saveRDS() saves it; the 2-D AnnData again as an
.h5ad and a .zarr.zip; the .nojekyll file GitHub Pages needs, a wet-lab-friendly README, one root-level
generate_datasets.py file that reproducibly renews them, and the policy files
any public repository needs (LICENSE, CONTRIBUTING.md, SECURITY.md,
SUPPORT.md, CODE_OF_CONDUCT.md). Deliberately, that is all: there are no
tests, validation harness, package scaffolding, workflows, .github/
directory, or site assets to copy. The README walks from one ordinary Python
prepare(...) call through the GitHub upload and optional GitHub Pages setup,
and names the exact catalog path and dataset links to try.
Important
Publishing data in a public repository publishes the coordinates, metadata, gene values, graphs, vector fields, and provenance that you include. Inspect the files for private identifiers before you push them. Use Server Mode (CLI + Python) for data that must remain private.
Try the reference repository first#
This takes about a minute and proves that the browser can reach GitHub raw content from your network.
Open Cellucid.
Expand Session in the left sidebar.
In the Link or server bar under Open data, paste the repository’s catalog path,
theislab/cellucid-demo-custom-datasets/catalog.Click Open (or press Enter).
Wait for the connection message, then choose one of the five entries in the dataset dropdown above the bar, whose label now reads “Dataset from
”.
The Link or server bar with the reference path entered. Type the catalog folder (or one file in it) and press Open.#
The shorthand reads the repository’s default branch (main, master or any
other), as GitHub resolves HEAD, without a request to find it; Cellucid never
probes branch names.
On success the dataset dropdown lists the catalog’s datasets in place of the
samples, labelled “Dataset from
Five tiny datasets, five useful checks#
Exact dataset ID |
Contents |
A useful first question |
|---|---|---|
|
72 cells, 8 genes, four |
Do the three |
|
96 cells, 10 genes, five |
Does the vector field follow both developmental branches when you orbit the view? |
|
48 cells, 6 genes, four |
How do |
|
60 cells measured twice: a MuData of the modalities |
Under Data mapping › Genes › Modality, does |
|
the same 60 cells as a Seurat object ( |
Does the object open as it was saved, with its assays as modalities? (Open R objects and MatrixMarket folders) |
Each dataset uses the navigation mode expected for its geometry: Planar in 1D
and 2D, Orbit in 3D. A catalog lists any file Cellucid opens, so the Seurat
.rds is listed like the Zarr folders; it is read once when it is listed or
opened, where the Zarr folders are read in place.
A dataset-specific link removes the final selection step:
https://www.cellucid.com/?data=gh:theislab/cellucid-demo-custom-datasets/catalog&dataset=synthetic-development-3d
Without dataset=, a link opens on the dataset the catalog names as its
default.
The repository contract#
The repository can contain source code and explanatory files, but the path you give Cellucid must resolve to a catalog folder with this shape:
my-cellucid-datasets/
├── README.md
├── generate_datasets.py
└── catalog/
├── datasets.json
├── study-a.zarr/
├── study-b.zarr/
└── study-a.cellucid-session # optional default view of study-a
The generator is the reference repository’s sole reproducibility script. Your own data repository may use a notebook or another preparation workflow instead; Cellucid itself only reads the catalog folder.
datasets.json is the exact catalog. Its top-level object contains only
schema, default, and datasets:
{
"schema": 2,
"default": "study-a",
"datasets": [
{
"id": "study-a",
"path": "study-a.zarr/",
"session": {
"path": "study-a.cellucid-session",
"bytes": 3256,
"sha256": "9465cfc1cd2dca59c0e9cc9c3ff3f4a26aac8dc57004eb51a641a2e437693c80"
}
},
{ "id": "study-b", "path": "study-b.zarr/" }
]
}
The rules are deliberately strict:
schemais exactly2.defaultis an exact dataset id present indatasets.datasetsis a non-empty array of entries holdingid,pathand, optionally,session; nothing else.Every
idis unique and follows the portable id rule (1–180 ASCII letters, digits,.,_or-, starting with a letter or digit, not ending with.). A file that states its own id (uns['cellucid']['id']) is listed under exactly that id.Every
pathis unique, relative to the catalog folder, cannot escape it, and names a Zarr store folder (ending in/), an.h5ad, or a.zarr.zip— or a file in any other format Cellucid reads, by its own ending (.h5mu,.loom,.rds, …).session, when present, names a.cellucid-sessionfile with its exactbytesand lowercasesha256.
Names, descriptions, citations and counts are not in the catalog: the dataset
list reads them from each file’s metadata and uns['cellucid'] when the
catalog is opened, and a dataset’s file is opened when it is chosen. The full
contract, including uns['cellucid'], is in
File format expectations.
Write the AnnData files#
From Python#
Use cellucid.prepare(...) once per dataset. Keep every array aligned to the
same cell order and give each dataset a stable portable id.
import cellucid
cellucid.prepare(
"catalog/study-a.zarr",
format="zarr",
embeddings={"X_umap": adata.obsm["X_umap"]},
obs=adata.obs,
var=adata.var,
gene_expression=adata.X,
connectivities=adata.obsp["connectivities"],
dataset_id="study-a",
dataset_name="Study A",
dataset_description="A public, documented study.",
citation_text="Author et al. (2026)",
citation_url="https://doi.org/10.0000/example",
)
The complete guide explains every argument: Preparing an AnnData for Cellucid.
Then write datasets.json by hand (it is a few lines), listing each file under
the id it states.
From R#
cellucid-r’s cellucid_prepare() writes the same Cellucid-ready AnnData.
Begin with R Package Guide.
From any AnnData file#
Any .h5ad or AnnData Zarr store can be listed as it is. A file that states no
id is listed under the catalog’s id; to carry its name, description and
citation, add uns['cellucid'] (or write it again with prepare(), which also
stores the expression matrix gene-major for fast gene reads).
Validate before publishing#
Test the exact files, not just the script that was meant to create them.
1. Test one file in the browser#
Open Cellucid, expand Session, click Open… under File or folder,
then Folder… (or File… for an .h5ad or .zarr.zip), and choose
catalog/study-a.zarr. Confirm:
the reported cell and gene counts;
every intended dimension and its default navigation mode;
representative categorical and continuous
obsfields;at least one gene;
the neighbour graph, if the file has one;
each vector field in each bound dimension.
Repeat for every dataset. Opening a file does not read datasets.json, so this
step isolates problems inside one file.
2. Test the complete catalog locally#
Serve the catalog folder with any static server that sends CORS headers, and
open ?data=http://127.0.0.1:8000/ in Cellucid. Select each catalog entry in
turn and repeat the checks above.
3. Inspect the public raw catalog#
After publishing, open this pattern in a browser:
https://raw.githubusercontent.com/<owner>/<repo>/<branch>/<path>/datasets.json
It must return the JSON file itself, not an HTML sign-in page, a Git LFS pointer, or a 404 response.
Publish and address the right branch#
Create a public GitHub repository.
Commit the catalog folder:
datasets.jsonand every file it lists.Push to the branch you intend to serve.
Open the raw catalog URL and then the Cellucid share URL.
For the repository’s default branch, use the compact form:
owner/repo/path/to/catalog
For a different branch, tag or commit, put @ref immediately after the
repository name:
owner/repo@release-2026/path/to/catalog
Do not write owner/repo/release-2026/path/to/catalog; that means the
release-2026/path/to/catalog folder on the default branch. Cellucid also
accepts an exact GitHub tree URL or exact raw.githubusercontent.com URL, both
of which already identify the branch.
If changing files in place would make an old result ambiguous, publish a new dataset id or a new branch and share a new exact URL. Saved sessions also check dataset identity, so stable ids should describe stable cell ordering and meaning.
GitHub Pages (with its required .nojekyll file), what each host costs per
request, the size limits, updating a published store, and Git LFS are in
Host a Dataset on GitHub. A Pages-hosted catalog is linked by its folder
address, ending in /:
https://www.cellucid.com/?data=https%3A%2F%2F<owner>.github.io%2F<repo>%2Fcatalog%2F&dataset=<exact-dataset-id>
Keep the download small and predictable#
The browser reads only what each view needs, but the choices made when writing the files still matter:
write only the genes and metadata fields the public visualization needs (
prepare(gene_identifiers=..., obs_keys=...));keep the expression matrix gene-major (CSC), as
prepare()writes it, so one gene is one contiguous read;prefer coarse categorical fields to high-cardinality ones, whose colours are not distinguishable;
preserve the original full analysis object outside the public files.
GitHub blocks files larger than 100 MiB, so publish each dataset as a Zarr store folder of small chunk files (Host a Dataset on GitHub explains why, with the limits and the Git LFS trade-off). For datasets beyond GitHub’s repository and Pages sizes, use a static object host or Server Mode (CLI + Python).
For a static host or CDN (a bucket, a lab web server), publish the same catalog folder with correct CORS headers (including range requests), then link to the encoded folder and dataset:
https://www.cellucid.com/?data=https%3A%2F%2Fdata.example.org%2Fcatalog%2F&dataset=study-a
The GitHub shorthand is specific to public GitHub raw content; it is not a
generic private-repository authentication mechanism. Every other way to open
data from a link (CELLxGENE, GEO, a single .h5ad file on any host) is in
Open from a link.
Fast diagnosis#
The most common mistake is pointing Cellucid at the repository instead of the catalog folder inside it. That produces one notification, The linked data could not be opened: https://raw.githubusercontent.com/…/ holds no datasets.json, so it is not a dataset catalog. …, naming the exact raw folder it read, which you can paste straight into a browser tab.
Connecting to a repository with the catalog folder left off. Opening the URL the notification names yourself confirms whether the problem is your path, your branch, or your network.#
Symptom |
Check first |
|---|---|
… holds no datasets.json, so it is not a dataset catalog |
The value must point at the catalog folder (or link one file or Zarr store folder instead); open |
|
The message names the key or value: an unknown key, a missing |
A dataset cannot be listed or opened |
The message names the dataset and the reason: a missing file (404), an id that differs from the file’s |
A request returns tiny text |
Check whether Git LFS stored a pointer instead of the file. |
A Zarr store opens locally but not from Pages |
Add |
It works locally but not on the lab network |
Test |
A saved session refuses the dataset |
Confirm the exact dataset id, cell count, and cell ordering are the same as when it was saved. |
Continue with Troubleshooting (data loading) for browser, CORS, memory, embedding, and server diagnostics. The reference repository’s README cross-references this guide and gives the shortest Python-to-GitHub path, including its single-file regeneration workflow, browser checks, privacy, updates, multiple datasets, and GitHub Pages.