An open-source, browser-based pixel-classifier for counting cells in microscopy images. Like Ilastik, but without the download or setup. It's GPU-accelerated and completely client side.
Tip
👋 Just here to count cells? Head to https://quantosaur.us/. No signup, no downloads, no installation. Get started as soon as the page loads! Everything below is for people building, deploying, or extending the tool itself.
Quantosaurus loads a microscopy image (.tif, .png, .jpg), and as you paint a few pixel labels per class, it trains a random forest on GPU-computed filter features and live-previews the classification across the whole image. Once you're happy with the result, GPU connected-component labeling turns the classified pixels into discrete object counts per class.
Everything — filter computation, random-forest training/inference, and connected-component labeling — runs in the browser via WebGPU, with an automatic WebGL2 fallback for browsers without WebGPU support. There is no backend, no server-side processing, and your image data never leaves the machine.
When to reach for it: after you've outgrown ImageJ's threshold tool, but before you need something heavier like Ilastik's Object Classifiers or Cellpose.
.tif,.png, and.jpgsupport- Live preview of classification as you label, thanks to GPU acceleration
- Per-class object counts via GPU connected-component labeling
- Segmentation, probability, and label exports
- Figma-style canvas navigation (pan/zoom)
- 3D support
-
Contrast sliders -
.ilpexport -
.ilpimport - Bulk inference.
A good tool should do one thing well. There is no plan to add neural networks or LLMs or AI. There is no plan to go beyond the most common bio-imaging formats like tif or zarr (see the roadmap). Suggestions are still welcome but keep in mind the Design Goals.
- No backend, no database. State lives in memory in the browser tab for the session; nothing is persisted or transmitted except what you explicitly export.
- Per-image GPU pipeline: a separable Gaussian-derivative filter bank computes 8 per-pixel features → a
FlatRandomForest(trained on the CPU from your labels, uploaded as a GPU buffer) classifies every pixel in a compute pass → connected-component labeling + a stats pass turn classified regions into per-object counts and metrics → a composite pass renders the overlay. - Two interchangeable GPU backends (
js/backends/webgpu.js,js/backends/webgl2.js) implement the same interface; WebGPU is preferred, with WebGL2 as a fallback for browsers that lack it. - Intensities stay in native units. Loaded pixel values keep the source image's raw range (e.g. 0–65535 for 16-bit TIFFs) rather than being normalized to 0–1, so contrast windowing happens on the GPU from real values.
For the full internals — data flow, the GPU backend contract, random-forest buffer layout, stats struct layout — see CLAUDE.md; it's written as engineering documentation, not just AI-assistant config.
- Accessible — no complicated UI, hidden affordances, or hotkeys to memorize for users; a flat, readable file structure for developers, with no
npmor install step so you cangit cloneand start hacking immediately. - Local — no active internet connection required to use it; no
node_modulesto dig through to build it. If a third-party library is genuinely necessary, it gets vendored into the repo rather than pulled in as a dependency. - Simple — does one thing well; readability was preferred over cleverness, and DIY was explored before reaching for a library.
There's no build step, no bundler, and no package manager — the code is plain ES modules served directly to the browser.
git clone https://github.com/Modjular/quantosaurus.git
cd quantosaurus
python3 -m http.server 8000 # or any static file serverThen open http://localhost:8000 in a browser. ES modules require serving over http(s)://, not file://. Edits to js/*.js, index.html, or style.css take effect on reload — no rebuild step.
A WebGPU-capable browser (recent Chrome/Edge) gets the full pipeline; other browsers fall back to WebGL2 automatically.
Core CPU logic (random forest, filter math, connected-component reference implementation, config invariants) has dependency-free unit tests colocated with each module (*.test.mjs), run with plain Node — no framework, no install step:
node js/rf.test.mjs # FlatRandomForest: training, flat-buffer layout, Gini/purity
node js/io.test.mjs # intensityToRGBA normalization
node js/backends/webgl2.test.mjs # CCL + stats reference implementations
node js/config.test.mjs # NUM_CLASSES / DEFAULT_LABEL_COLORS invariantsGPU-dependent code (actual draw/dispatch calls in the WebGPU/WebGL2 backends) isn't unit-testable this way and needs manual verification in a browser. There is no CI configured yet.
Quantosaurus has no environment variables, API keys, or external services to configure — it's a static, self-contained app. Tunable constants (forest size, feature count, training debounce, camera zoom bounds, stats buffer layout) live in js/config.js and are read by both the JS and the WGSL/GLSL shaders; change values there rather than inlining them elsewhere.
Bug reports and pull requests are welcome. Before opening a PR:
- Run the test suite above (
js/rf.test.mjs,js/io.test.mjs,js/backends/webgl2.test.mjs,js/config.test.mjs) and confirm everything passes. - Manually exercise the change in a browser — GPU code paths in particular can't be caught by the unit tests.
- Keep new app-logic code dependency-free and in the existing style: functions take
stateexplicitly rather than closing over globals, and exported functions carry JSDoc comments. SeeCLAUDE.mdfor the full conventions.
If a third-party library is truly necessary, vendor it into js/vendor/ rather than introducing npm/a build step — see how itk-wasm-image-io and jszip are handled there.
Thanks to Ilastik for inspiration. Thanks to ITK-Wasm for file handling.