Methods, validation and citation
CytoWeave reimplements methods from published papers and preprints. This page shows how its results are checked against answers known in advance and against the tools those methods come from, how to cite it, and whose work it builds on.
Validation
The unit tests check every analysis function against hand-computed, published or reference-tool values. The validation suite runs the complete pipelines, the way they run in the app, against answers known in advance:
| Area | Checked against | Result |
|---|---|---|
| FCS | All 290 example files | Parsed without warnings; written and read back bit-exact |
| FCS fuzzing | 20,000 mutations of files in every layout (offsets, keywords, delimiters, flipped bytes, truncation) | Every file read consistently or refused with a message; no crash, hang or outsized allocation |
| Compensation | The true spillover of the PBMC example | Matrices within 0.02 of the truth; the planted error found first, corrected to 0.158 (true 0.157) |
| Gating | True cell types | Precision 91–100%, recall 96–100% |
| QC | Known clogs, bubbles and drift | 99.8–100% of anomalous events removed; ≤ 1.4% of clean events, none from clean or drifting files |
| Spectral | True abundances of 25 fluorochromes | Median r = 0.988; within 0.001 of unmixing with the true spectra |
| Unmixing doctor | Six faults planted in the spectral example; real bead and cell controls and fixed spleen (the AutoSpectral example data of Burton, Dooley and Liston) | Each fault named first on two experiments, nothing on clean samples, each fix closer to the truth; the autofluorescent cell controls and the fixed spleen named |
| Cell cycle | True phase fractions | Dean–Jett–Fox within 1.6 points, Watson within 2 |
| Proliferation | True precursor frequencies | Division index within 3% |
| Kinetics | Every cell's calcium in a simulated flux (five tubes) | Peak and times within one bin and 3% of the noise-free curve; responding share within 0.2 points |
| Plates and dose-response | A simulated 96-well screen with known IC50s; R's drc 4.0 on 8 synthetic curves and the screen | Every well placed from keywords and from names; Z′ equal to the true shares'; each simulated IC50 inside its 95% CI; residual sums never above drc's, drc from CytoWeave's estimate within 1e-8, standard errors within 1e-5 of the exact Hessian |
| Bead immunoassays | A simulated LEGENDplex-like 8-plex with known concentrations; beadplexr 0.5 and drc on its curves; beadplexr's own LEGENDplex data (13-plex, 18 files) | 99.6% of beads assigned to their analyte; sera in range within 20% for 97% (median 1.8%); concentrations equal to drc's within 1e-8; bead identification identical to beadplexr's on 17 files, the 18th where its clustering merged two analytes |
| Debarcoding | True wells of a 20-sample barcoded plate | 100% of assigned cells in their true well; 98.7% of cells assigned |
| Clustering | 23 true populations | FlowSOM adjusted Rand index 0.91 |
| Normalization | Same-donor anchors in two batches | Batch distance reduced 25× |
| Scales | BD's FlowJo lookup tables | Biexponential within 5e-6 (the tables' precision) |
| Statistics | R 4.x | t-tests, Wilcoxon, Benjamini–Hochberg and t quantiles agree |
| Autogating | True cell types of a cohort with instrument shifts up to fivefold | Every shifted gate more accurate (T cells F1 0.954 → 0.994), none less; the 4 least accurate of 66 sent to review; nothing sent to review without a shift |
| Instrument (Q and B) | 30 simulated bead runs with known Q and B, and a PMT, flow cell and laser problem | Q within 2% (median), B within 6%; every problem flagged at once on Levey–Jennings charts, none in the 20 baseline runs |
| Spectral library | A second experiment's controls, and a tandem that lost 5% of its emission | Independent controls all match (largest difference 0.01); the degraded PE-Cy7 flagged and nothing else; a library spectrum unmixes as well as the dye's own control |
| Figure provenance | A 60-plot figure of 12 samples | Read back intact from SVG, PNG and PDF; rebuilt with every plot drawn from the same events |
| Predicted spread | Simulated controls with known photon noise and laser fluctuations | Photon noise within 1% of the truth; each control's spread predicted from the other 24 within 2× for 98% of pairs; a 15-dye panel predicted from the 25-dye fit within 2× for every pair |
| Comparisons with a control | flowStats and R on tubes with known positive fractions; Bagwell's simulation | Probability binning identical to flowStats (36 cases, to 9e-15); SED within 2 points of the true fraction, Overton up to 8 points under where populations overlap; T(χ) above 4 in 0.5% of samples of the same cells |
| Formula channels | R evaluating the same 7 formulas on 10 tubes; Gating-ML and a template | Medians within 5e-8, single events within 2e-14; a ratio gate the same through Gating-ML (fratio) and on renamed detectors |
| Calibrated units (MEF) | FlowCal 1.3.1 on its own bead and cell files; simulated beads of known response | The same levels left out, medians within one step of the log channel, cells' MEFL within 1.6% of FlowCal's; a known slope within 0.001 |
| Absolute counts | 40 simulated tubes of known concentration with counting beads | Within 1% on average, scattered as Poisson counting predicts |
| Differential state | diffcyt 1.32 and limma 3.68 in R on the mass cytometry examples (8 samples with a batch, 20 paired wells); limma on 11 synthetic cases; activation known in advance | Every count and median identical, every moderated t, p and adjusted p within 3e-11; the known activation changes found (15 of 15 strong ones) with 3 of 53 calls false; no call where no marker differs |
| Events in and out | Concatenated, downsampled and per-sample FCS files against their sources; CSV files from CytoWeave, FlowJo and European locales, and a damaged one; AnnData files read by anndata 0.10 and 0.13, h5py, pyfive, fcsparser and FlowIO | Every event its source's, every population counted alike per SampleID; seeded downsampling exact and uniform; CSV values back exactly, every fault reported; X, obs and maps read exactly by every reader |
| Batch reports and spreadsheets | A figure repeated by sample and by subject, every number recomputed; the PDF, PowerPoint, Excel and Prism files read by openpyxl, python-pptx, pypdf and R's pzfx | Every plot where the rules put it; all 171 numbers equal to their table column or gate; no untraced number in the PDF; workbook, deck and Prism values exact in every reader |
| Headless runs | The PBMC example's 12 samples and a template run twice with cytoweave run, and analyzed in the window with its own buttons; Node on the same files | The same outputs both times and as the window exports them (CSV bytes, workbook values, report text, workspace, methods); all 228 counts equal to Node's |
| Virtual FMO controls | Simulated LSRFortessa- and Aurora-like panels with FMO tubes of the same donor's cells (15 markers); public FMO controls of a BD LSRFortessa panel (7 tubes, Zenodo 22808501) and a 25-color Cytek Aurora panel (4 tubes, Zenodo 20644656), each predicting its own channel | Simulated: within 1% of the display axis on average (worst 2.4%), against 12–13% for the unstained control; real: 5.8% (worst 11.6%) and 5.5% (worst 14.8%) of the axis. Where an FMO leans against another channel (a compensation error), it shows what spread does not predict |
| Panel optimizer | Simulated 8-marker T-cell panels on 10 dyes (an Aurora, unmixed, and an LSRFortessa, compensated): every assignment stainable, each marker's resolution measured on cells with its co-expressed markers; random small panels solved exhaustively; a 25-color design on 29 dyes | Predicted against measured cost: r = 0.996 and 0.999 (log) across 26 assignments; the optimum measured best, 4× and 29× less noisy than dimmest-on-brightest; local search found every exhaustive optimum; a 25-color design in about 8 s, two seeds equal to a search 4× longer |
| Review reports | Every example's report with every sample's gates drawn, a figure, a table and a saved comparison; a run's report opened in Chrome | All 2,892 counts, table cells and plot percentages equal to the window's own and shown as it shows them; nothing requested beyond the file; no WCAG 2.1 AA violations in either theme |
| Reproducibility certificates | Every example certified with its gates, a table, k-means clusters stored as a channel and a saved comparison, read back and verified; then six kinds of tampering; certificates made in Chrome verified in Node and the reverse | All 2,413 numbers identical, bit for bit; the same archive byte for byte when made twice; every change caught (a bit of a file or stored channel, a gate moved 1%, a log entry altered or removed, an edited number); across engines, confidence limits equal to 12 digits |
| Rare events | R's exact intervals; simulated blanks and low-level samples | Intervals equal to poisson.test and binom.test, covering ≥ 95%; EP17 limits flagging 4% of new blanks and detecting 98% at the limit of detection |
| Robustness to analysis choices | Comparisons with known answers: a real effect, a gain shift, clogs and a stale matrix in one group, and no effect | The real effect holds in 64 of 64 analyses; each artifact called fragile or traced to the choice behind it; under no effect, half of the chance findings are flagged |
| Accessibility | Every text color on every surface, the palettes in simulated color-vision deficiencies, and axe-core in 86 pages | Contrast ≥ 4.5:1 everywhere in both themes; friendly palette ≥ 11 apart (CIEDE2000) in every kind of vision; no axe-core violations |
| Titration and voltage walks | A simulated CD4-PE titration and PE voltage walk with known binding, noise and gain | Stain index within 4.4% of the true cells' at every step; the recommended amount the binding's; the voltage range within 1 V of the truth; medians equal to FlowJo 11's on the same files |
| Templates | A 19-population analysis applied to the same events with every detector renamed and reordered | Every population holds the same events in all 12 samples; with two markers unnamed, only the gate on them left out |
| Published strategies | OMIP-101 and OMIP-090 placed on one sample of the PBMC example, against the true cell types | Median F1 0.96–0.99 for lineages, 0.88–0.96 for memory subsets, NK cells and classical and non-classical monocytes; Tregs 0.91; intermediate monocytes 0.76 |
| Gating-ML | ISAC's compliance suite | All 190 gates match on every event |
| FlowJo | FlowJo's saved counts in 14 workspaces, and FlowKit's | The bundled example and FlowKit's synthetic workspaces exact; real 8-color workspaces at least as close to FlowJo as FlowKit |
| FlowJo export | The workspace imported back, FlowKit and CytoML reading the export, and FlowJo 11.2.0 (build 11.2.0.210156) opening three exports | Every count unchanged in 12 workspaces; FlowKit counts what CytoWeave counts (ellipse boundaries aside), CytoML 306 of 313 counts equal; in FlowJo 11 every population within 0.6 percentage points, most within 0.1 |
| De-identification | Every example and corpus FCS file | The same events, bit for bit |
| Reference tools | FlowKit 1.3.2 and FlowIO | FCS decoding, compensation, spectral unmixing and transforms agree |
| FCS files | 16 instrument and malformed test files | All readable files read and written back bit-exact; malformed ones refused with a clear message |
| FCS files from 42 more instruments | 47 public files (CytoFLEX, NovoCyte, Aurora, Sony, FACSDiscover S8, FACSymphony, ZE5, Attune, Accuri, Helios and others), against FlowIO and fcsparser | Every file read; values within 4e-7 of both readers, and right where they are not (stale offsets, FCS 3.2 integer channels, log channels stored as decades) |
| R packages | flowCore, PeacoQC 1.22, FlowSOM and CytoNorm in R, on their example data and other public files | Values read, compensated and logicle-scaled within 1e-7; PeacoQC (classic) removes the same events; FlowSOM maps every event alike and agrees with R as closely as R agrees with itself; CytoNorm within 1e-5 |
| BD FACSDiva | Its spillover matrix from 15 real single-stain controls | Every entry within 0.015 (median method), with no manual gating |
| Autogating, against experts | An expert's per-donor gates in 4 FlowJo workspaces of a cytokine study (48 wells) | Agreement with the expert unchanged (F1 0.9876 → 0.9877), no adjustment lowering it; wells gated differently sent to review 3× as often as the others |
| flowQB | flowQB on its own LSR II data: an LED series, 8-peak and 6-peak beads | The same peaks, Q, B and standard errors in all 36 detectors (within 6e-9) |
| Predicted spread, real controls | A BD LSRFortessa's 15 bead controls, each predicted from the other 14; a Cytek Aurora's 25 bead references (Zenodo 20644656), a 12-dye panel predicted from the noise of the other 12 dyes' controls | Within 2× of the observed spread for 79% of well-measured pairs (median ×1.32); on the Aurora, 65% and 73% each way (r = 0.76 and 0.80; median ×1.6) |
| Robustness, real study | 4 donors of an intracellular cytokine study | Every PMA comparison holds in all analyses; one small IL-4 peptide response fragile |
| Cell Ontology | 40 populations experts named in 3 public workspaces | Every top suggestion a term the name denotes; the same with every gate renamed |
The public test data behind the last twelve rows are not part of the program: node validation/fetch.mjs downloads them and checks their checksums. The tolerances, the notes on each comparison and how to run the suite are in the validation README.
What a simulated truth can and cannot show
The bundled examples are generated by a simulator that keeps the truth for every event: its cell type, its fluorochrome abundances, whether it falls inside a clog or bubble, and the true spillover, phase fractions, precursor frequencies and batch effects. That tests the software against answers known exactly, which real data cannot provide. It does not test whether the simulator is realistic. The simulator models what makes real analyses hard: spillover and spreading from photon statistics, lymphoid and myeloid autofluorescence, dead cells, debris and doublets, clogs, bubbles and drift, and batch effects. Real data can still fail in ways it does not model, which is why the suite also runs on real instrument files, real FlowJo workspaces and the R packages these methods come from.
Performance on large samples
At ten million events (an 801 MB file with 21 parameters), on an Apple M4 laptop:
| Stage | Time |
|---|---|
| Read the file in 16 MB parts | 0.75 s, holding only the columns |
| Evaluate six gates, cold | 2.0 s, including the channels and scales they use |
| Move the top gate, re-evaluating every population | 0.43 s |
| Memory of the six populations | 7 MB |
| Statistics table, 14 channels of 6.7 million events | 0.78 s |
| Acquisition QC, in a background worker | about 70 s, with the window responsive throughout |
node --expose-gc validation/bench.mjs 10000000 reproduces these numbers; it needs no downloads.
Methods it reimplements
CytoWeave's analyses are reimplementations of methods from published papers and preprints. Their authors are cited in the code and in the methods paragraph CytoWeave writes for your analysis, among them:
- Logicle: Parks, Roederer and Moore
- PeacoQC: Emmaneel et al.
- FlowSOM and CytoNorm: Van Gassen et al.
- UMAP: McInnes et al.; t-SNE: van der Maaten and Hinton
- Leiden: Traag et al.; Louvain: Blondel et al.
- k-means++: Arthur and Vassilvitskii; Hamerly's k-means
- The spillover spreading matrix: Nguyen et al.; compensation by robust regression: AutoSpill, Roca et al.
- Spectral unmixing: Novo, Grégori and Rajwa; multiple autofluorescence signatures: Roet et al.; per-cell autofluorescence matching: AutoSpectral, Burton et al. (bioRxiv preprint, doi:10.1101/2025.10.27.684855)
- Marker enrichment modeling (MEM): Diggins et al.
- Trustworthiness and continuity: Venna and Kaski; LISI: Korsunsky et al.
- MIFlowCyt, FCS and Gating-ML: the International Society for Advancement of Cytometry (ISAC)
- Stain index: Maecker et al.; separation index: Bigos; titration: Bonilla et al.; voltage setup: Meinelt et al. (BD)
- Probability binning: Roederer et al.; Baggerly; histogram subtraction: Overton; enhanced normalized subtraction (SED): Bagwell
- Exact intervals: Garwood; Clopper and Pearson; detection limits: CLSI EP17, Armbruster and Pry; events needed: Roederer
- Calibrated units from beads: FlowCal, Castillo-Hair et al.; absolute counts: Brando et al.
- Differential state: diffcyt, Weber et al.; moderated t-statistics: limma, Smyth; Ritchie et al.; Chen et al.
- The OMIP-101 and OMIP-090 gating strategies: Imbratta et al.; Stroukov et al.; immunophenotypes: Maecker, McCoy and Nussenblatt
The Report view lists the references for the analyses you actually used, with DOIs and a BibTeX file; see the methods paragraph.
Cite CytoWeave
If you use CytoWeave in published work, please cite it together with the methods listed in its Report view. The repository's CITATION.cff holds the citation, and GitHub's Cite this repository button formats it.
@software{cytoweave,
author = {McDermott, Robert},
title = {CytoWeave: a flow cytometry analysis workbench},
version = {0.8.1},
url = {https://github.com/robert-mcdermott/cytoweave},
license = {Apache-2.0},
year = {2026}
}Privacy and security
- Files are parsed and analyzed in the browser. Nothing is uploaded, and CytoWeave makes no network requests of its own.
- By default the server listens only on the loopback interface, rejects requests whose
Hostheader is not a local address (which blocks DNS rebinding), accepts API calls only from its own pages, and serves a strict Content Security Policy. - Remote control is off unless
--remote-controlis given (cytoweave mcpturns it on for its own session). Actions that read or write files need a per-session token, which CytoWeave also writes to a file in its data folder that only you can read, for the R and Python clients.
Research software. CytoWeave is not a medical device and must not be used for diagnosis.
Credits and trademarks
CytoWeave is written by Robert McDermott and licensed under the Apache License 2.0. Ported code:
- The FlowJo biexponential algorithm is ported from FlowKit (BSD-3-Clause, Scott White), which ported it from cytolib.
- The moderated t-statistics follow the R and C sources of limma 3.68.5 (GPL ≥ 2, Gordon Smyth and co-authors) and statmod 1.5 (Gordon Smyth), so that results equal R's.
- The validation suite includes excerpts of BD's FlowJo transformation lookup tables (MIT license, © 2020 Becton, Dickinson and Company).
Cell types are named with terms of the Cell Ontology (release 2026-06-08). CytoWeave contains the labels, synonyms and definitions of 89 of its terms, under the Creative Commons Attribution 4.0 license: Tan SZK et al. The Cell Ontology in the age of single-cell omics. Sci Data 13, 946 (2026), doi:10.1038/s41597-026-07173-8.
AnnData files follow the anndata project's on-disk format specification (scverse); their HDF5 is written by CytoWeave from the HDF5 file format specification (The HDF Group).
PDF text is set in Helvetica with Adobe's metrics for the standard PDF fonts, as reportlab lists them. Prism projects follow the layout of GraphPad's own files, as the R package pzfx (Yue Jiang) reads and writes them.
FlowJo, FACSDiva, FACSDiscover, BD, Cytek, Aurora, Northern Lights, Sony, ID7000, Amnis, CellView, CellTrace, GraphPad Prism, Microsoft Excel and PowerPoint and the other product and company names on this site and in CytoWeave are trademarks of their respective owners. They are named only to say what CytoWeave works with, such as the files and workspaces it reads. CytoWeave is an independent open-source project, not affiliated with, endorsed by or sponsored by any of them.
The example experiments and every screenshot on this site are simulated data. Instrument names in them (such as "LSRFortessa-like") describe the configuration that was modeled, not data from that instrument.