Why We Built Cytogence FCS in Rust
The technical reasoning behind choosing Rust over Electron, Python, or Java for a desktop flow cytometry application — and what it means for researchers analyzing large FCS files.
Most scientific desktop software is built on one of three stacks: Java (FlowJo, CytoBank), Python (custom scripts, notebooks), or Electron (web-tech wrapped in a desktop shell). Each has tradeoffs. We chose a fourth path — Rust — and it’s the best decision we made.
Here’s why.
The Problem with the Status Quo
Java (FlowJo’s Foundation)
FlowJo is the gold standard for flow cytometry analysis, and it’s built on Java. Java is mature, cross-platform, and well-understood. But it carries a runtime overhead — the JVM consumes memory, startup can be slower, and garbage collection and runtime overhead can contribute to UI latency when working with large datasets. For a 100K-event FCS file, this rarely matters. For a 10-million-event spectral flow experiment, it does.
Python (The Bioinformatics Default)
Python dominates bioinformatics scripting. Libraries like FlowCytometryTools and FlowUtils make it straightforward to read FCS files and run basic analyses. But Python can struggle with interactive performance for computation-heavy tasks (gating calculations, compensation matrix operations, clustering) unless critical paths are implemented in optimized native code. It also requires users to install and manage a Python environment, and building a polished desktop GUI in Python remains painful.
Electron (Modern UI, Heavy Cost)
Electron lets you build desktop apps with web technologies — HTML, CSS, JavaScript. The UI looks modern, but each Electron app bundles an entire Chromium browser. That’s 150-200MB of overhead before your application code even loads. Memory usage is high, and performance can be constrained for compute-heavy workloads without additional native modules or parallelization strategies.
Why Rust
Few languages offer the combination of properties we needed in a single stack. Rust gives us three key advantages simultaneously:
1. Native Performance
Rust compiles to native machine code — no virtual machine, no interpreter, no garbage collector. Gate evaluation, compensation matrix multiplication, and clustering algorithms run at C/C++ speed. When a researcher loads a file with 500K events and draws a polygon gate, the population statistics update in real time — not after a loading spinner.
Our goal wasn’t just speed — it was consistent, predictable performance during interactive analysis. In internal benchmarks, Rust-based implementations consistently outperformed equivalent Python and Java approaches for FCS parsing and repeated analytical operations. The exact speedup depends on dataset size and operation type, but the impact is most noticeable in interactive workflows where every gate adjustment triggers a cascade of recalculations.
2. Memory Safety Without Garbage Collection
Rust’s ownership system prevents memory leaks, null pointer dereferences, and data races — at compile time, not runtime. This means no garbage collection pauses that cause UI freezes, no memory leaks that accumulate during long analysis sessions, and no mysterious crashes when working with large datasets.
For a desktop application that researchers use for hours at a time, memory predictability matters. An application that gets slower the longer you use it is an application people stop trusting.
3. True Cross-Platform
We use Tauri as our desktop framework — Rust backend with a native webview for the UI. The total application binary is approximately 5MB, compared to Electron’s 150MB+. The same codebase compiles and runs identically on Windows, macOS, and Linux without platform-specific workarounds.
What This Means in Practice
FCS File Parsing
Our FCS parser handles 2.0, 3.0, and 3.1 files with all data types (integer, float, double, ASCII). It reads spillover matrices, extracts all metadata keywords, and classifies parameters (scatter vs. fluorescence vs. time) — all in the time it takes other tools to show a progress bar.
Gate Evaluation
The gating engine evaluates hierarchical gate trees using depth-first traversal. Boolean operations (AND, OR, NOT) across gate combinations resolve in microseconds. When you adjust a gate boundary, every downstream statistic updates in real time with minimal latency — no “recalculate” button needed.
Clustering
Our ML auto-gating implementations (K-means, DBSCAN, Gaussian Mixture Models) run entirely in Rust via the linfa machine learning framework. BIC-based model selection for GMMs — which requires fitting multiple models and comparing them — completes in seconds rather than minutes.
GPU-Accelerated Rendering
The scatter plots, histograms, and density plots in Cytogence FCS are rendered via PixiJS with WebGPU/WebGL support. Rust handles the data processing and transformation; the GPU handles the rendering of visualizations. The result is smooth, interactive visualization of 100K+ events without lag.
The AI Layer
One question we get: if the core is Rust, how does the AI copilot work?
The AI integration runs as a separate async layer using Tokio (Rust’s async runtime). LLM API calls (to Claude, GPT-4, Bedrock, or local Ollama models) happen in the background without blocking the analysis engine or UI. A growing set of tools — creating gates, computing statistics, suggesting strategies — call directly into the Rust analysis core. The AI suggests; Rust executes.
What This Is Not
This isn’t about replacing Python for research workflows or claiming Rust is universally better. Python remains the best choice for exploratory analysis, scripting, and leveraging the vast BioConductor/Bioconda ecosystem. Java powers production-grade tools that millions of researchers rely on daily.
Our argument is narrower: for building a fast, reliable, interactive desktop application that handles large-scale cytometry data — where every millisecond of UI responsiveness matters — Rust is the right tool for this specific problem.
The Tradeoff We Accepted
Rust is harder to write than Python or JavaScript. The learning curve is steep, compile times are longer, and the ecosystem for scientific computing is younger than Python’s. We don’t have the equivalent of every BioConductor package at our fingertips.
But for a desktop application where performance, reliability, and user experience are paramount — where researchers need to trust that their tool won’t crash, won’t slow down, and won’t mishandle their data — those tradeoffs are worth it.
We handle the complexity so researchers can focus on the biology.
Cytogence FCS is a next-generation desktop application for flow cytometry analysis. Learn more or request a demo.