Local development setup#
This page gives a repeatable, low-friction way to run the Cellucid web app locally for debugging and development.
Because the app is shipped as native ES modules (no bundler required), the setup is usually just “run a static server and reload”.
At a glance#
Audience
Wet lab / non-technical: you can use this to reproduce bugs and capture screenshots, without writing code.
Computational users: use this to run on the same machine as
cellucid-pythonserver mode.Developers: use this for rapid iteration; then read Debugging playbook.
Time
2–5 minutes for “hello world” local run
10–20 minutes if you also want to test remote server + Jupyter workflows
Prerequisites
A current stable Chrome, Edge, Firefox, or Safari release with WebGL2
Python 3 (for a simple local static server), or any equivalent local server
Step 0: Know the two “local” modes#
There are two distinct local workflows:
Local web app: you run the frontend from this workspace (
cellucid/), and you load demo or local files.Local data server: you run
cellucid-pythonto serve a dataset, and the frontend connects to it (either from your local web app or fromhttps://www.cellucid.com).
They are often used together, but debugging is faster when you can clearly say which one you’re using.
Step 1: Run the Cellucid web app locally#
From the workspace root:
cd cellucid
python -m http.server 8000
Then open:
http://localhost:8000
Important
Do not open cellucid/index.html via file://.
Browsers will block ES module imports and cross-origin fetches in ways that look like “random failures”.
Always use an HTTP server.
Step 2: Turn on debug logging (recommended)#
In DevTools → Console:
localStorage.setItem('CELLUCID_DEBUG', 'true');
location.reload();
To disable:
localStorage.removeItem('CELLUCID_DEBUG');
location.reload();
What this does:
Enables the lightweight logger in
cellucid/assets/js/utils/debug.js.You’ll see prefixed logs like
[Cellucid] ...across data/state/UI flows.
Step 3: Load a dataset (choose one)#
Option A: Use built-in demo datasets (fastest)#
If the app can fetch datasets.json from the configured sample catalog, the dataset dropdown will populate from the local-demo source.
Configure the sample catalog via:
<meta name="cellucid-sample-catalog" ...>incellucid/index.html, or?sampleCatalog=...as a runtime override (useful in development): one absolute folder URL ending in/that holds adatasets.json.
This is best for:
UI work
rendering/LOD work
session/export work
Option B: Use the browser file picker (local-user)#
Use Open… under Open data → File or folder to select:
a file (File…):
.h5ad,.h5mu,.loom, a 10x Cell Ranger.h5, an R object (.rds,.rda/.RData, gzipped or not), or a.zarr.ziparchive holding one AnnData or MuData Zarr store, ora folder (Folder…): a
.zarrstore (AnnData or MuData), a Cell Rangerouts/folder, or a MatrixMarket folder
or drop one of them anywhere on the page. The formats are the entries of
data/input-formats.js.
This is best for:
“real user” workflows with no Python server running
testing permission/UX issues around file pickers
Option C: Connect to a local Python server (cellucid serve)#
Run (in another terminal) from your cellucid-python environment:
cellucid serve /path/to/dataset.h5ad --dataset-name "My dataset" --dataset-id my-dataset
# or
cellucid serve /path/to/dataset.zarr --dataset-name "My dataset" --dataset-id my-dataset
Then in the Cellucid UI, type the server’s address into the Link or server bar under Open data and click Open:
127.0.0.1:<port>,http://127.0.0.1:<port>, or just<port>
This is best for:
debugging the served-AnnData path (
data/remote-source.js,data/served-anndata.js): the browser reads the store in place with range requests, and the server only serves its bytesZarr store folders, which the server serves key by key
any other format of the registry (
.h5mu,.loom, 10x, MatrixMarket, R objects): the server serves the bytes and the browser reads themreproducible debugging (server logs + client logs)
Note
If you are using the hosted app (https://www.cellucid.com) and a local server, you are doing an https → http connection.
Most modern browsers allow https://… pages to talk to http://localhost / http://127.0.0.1, but if your environment blocks it, run the web app locally (http://localhost:8000) and connect to the server from there.
Option D: Notebook viewer (hooks + events)#
If you are developing the Python↔frontend hooks system:
Read the Python docs section: Jupyter Hooks System (Python ↔ Frontend)
Step 4: Make local iteration fast (recommended defaults)#
Disable cache while debugging#
In Chrome/Edge DevTools:
Network tab → check “Disable cache”
Keep DevTools open while reloading
This avoids “I fixed it but it still reproduces” confusion caused by cached ES modules.
Every script URL carries its content version (?v=, written by
node scripts/generate-web-asset-inventory.mjs; see
Build, run, and deployment, checklist item 5). The server ignores it, so
an edited module loads without running anything, as long as the browser does
not answer it from its cache; once the generator has run, each changed module
has a new URL and a plain reload loads it.
Workers start from their sources too while the page is served from this
machine (localhost, 127.0.0.1, [::1]), so an edited worker module is live
on the next reload. A deployed site starts them from the bundles the generator
builds into assets/workers/; to try those locally, run the generator and open
the same server as http://deployed.localhost:8000 (every browser resolves a
.localhost name to this machine).
Keep reproducible datasets handy#
For bugs that involve state/persistence, keep a small “debug dataset” around:
~5k–50k cells (fast reloads)
at least one categorical field with many categories
at least one continuous field with outliers and NaNs
optionally one vector field overlay
This helps you reproduce edge cases quickly without waiting for large loads.
Troubleshooting#
Symptom: “Blank page / canvas is black”#
First, check whether you got a full-screen startup failure card. If you did, the bootstrap failed deliberately: the heading names the kind of problem and Details holds the exact reason — read it before anything else. A truly blank page means the module graph never ran at all, which is a serving problem, not an application one.
Likely causes (ordered):
WebGL2 not available (browser/GPU issue) — you will get the card headed “This browser cannot run Cellucid”, with “WebGL2 is required but not supported in this browser.” under Details.
A JS exception during startup prevented viewer initialization.
The app cannot fetch required files due to server/CORS/missing paths.
How to confirm:
DevTools → Console: look for the first error after “Starting…”.
DevTools → Network: reload and look for
404or blocked requests.
Symptom: “Module script failed to load”#
Likely causes:
You opened via
file://, or the server is sending incorrect MIME types for.js.
Fix:
Use
python -m http.serverfromcellucid/(Step 1).
Symptom: “Remote server connect fails / CORS error”#
Likely causes:
The server is not running.
The server does not send CORS headers for the Cellucid origin you’re using.
Corporate proxy/ad blocker interferes with localhost requests.
How to confirm:
Open the remote server URL in a new tab and see if it responds.
Check DevTools → Network for failed
fetchrequests and CORS messages.
Fix:
Use the locally hosted app (
http://localhost:8000) and connect from there.Or configure the server origin/CORS (Python side).
Next: Build, run, and deployment.