Skip to content

Repository files navigation

GateLabR

DOI License: MIT Docs

Interactive manual gating for SingleCellExperiment objects in R.

Open-source flow cytometry and CyTOF (mass cytometry) gating app for R — with FCS import, fluorescence compensation, hierarchical population trees, and Gating-ML exchange, all persisted inside the object.

📖 Documentation & Getting Started

GateLabR runs the same TypeScript/React interface as GateLab, backed by a thin R host for hand-gating flow cytometry and mass cytometry (CyTOF) data directly on SingleCellExperiment objects. It slots into Bioconductor-based analysis pipelines (e.g. CATALYST, diffcyt) where the events already live in R as an SCE and you want a fast, reproducible alternative to round-tripping through FlowJo or Cytobank just to draw a few gates.

Gates are drawn on an interactive D3.js canvas. Gates, populations, scales, assay bindings, compensation provenance and illustration settings persist in the SCE via metadata(), while explicit commands write panel labels to rowData() and population, division or sample annotations to colData().

For the same style of manual gating directly from FCS files without an R installation, see GateLab, the companion browser app. GateLabR is the better fit when data already live in a SingleCellExperiment or when gated populations need to return directly to colData for a Bioconductor pipeline.

Features

  • Draw and edit gates interactively. Polygon, rectangle and quadrant gates on any pair of channels, with click-and-drag vertex editing, undo / redo, and per-gate colour and label.
  • Population trees. Build hierarchies of populations from gate references — each population is the intersection (AND) of its gates within its parent, and a population may exclude a gate (NOT); counts and percentages update live.
  • Flow and CyTOF modes. Auto-detects the instrument type from channel names. Flow uses per-channel logicle for fluorescence and arcsinh for FSC / SSC scatter, with editable W and cofactor; CyTOF channels use arcsinh (cofactor 5).
  • Flow and CyTOF compensation. Inspect, edit, sweep and apply spill matrices in the shared Compensation tab. GateLabR performs the durable calculation in R, installs a revisioned full-size SCE assay atomically, preserves the original assay, and restores saved compensated assays without recomputation. Existing compensated SCE assays can be adopted directly.
  • Cytobank-compatible Gating-ML 2.0 import / export. Exchange gates and population trees with Cytobank and other ISAC Gating-ML 2.0 tools. Import is done by the embedded GateLab core in the browser, not in R: it reads rectangle, range, polygon and ellipse gates, AND populations, and the Boolean NOT of a gate. A population's excluded gate is read from the Gating-ML 2.0 schema's gating:use-as-complement="true" on the gate's reference, which GateLab now writes in its Cytobank format, and from gating:complement="true", an attribute outside the schema that GateLab and GateLabR wrote before. An OR population is left out, with everything beneath it, and named in a warning, and the rest of the file is imported; so is a population that uses a gate placed beneath a population that is not its ancestor, which GateLab cannot represent within one parent. Files containing other gate types, transformations GateLab does not read, or gates whose channels cannot be matched to the loaded data are refused before import. (FlowJo uses its own .wsp format and does not round-trip Gating-ML.)
  • Workspace persistence. Gates, populations, scales, active assay, compensation provenance and illustration settings are saved inside the SCE and re-loaded automatically.
  • FCS export. Export gated populations as FCS files, optionally split by sample_id.
  • Sample filter and multi-sample overlay. Filter by any colData column and overlay multiple samples with distinct colours.
  • Colour by a colData column. Any factor, character or logical column with up to 254 levels (a FlowSOM or CATALYST merge level, say) appears under "Colour by", so you can watch cluster composition change while you drag a gate. Values are fetched only when a column is chosen. Set metadata(sce)$gatelab_palettes$<column> to a named colour vector to make the app use the same colours as your figures.
  • Statistics tab. Per-population, per-channel summary stats (count, % parent / total, median, mean, geometric mean, SD, CV) exportable to CSV.
  • Cell-division profiler. A CFSE / CellTrace dye-dilution tab: draggable per-sample division gates on a 1-D histogram (Div0…DivN), a marker-vs-dye biplot with density contours, and per-cell division calls written back to colData.
  • Composition preview. A Proportions tab for quick stacked-bar and boxplot previews of any colData composition (e.g. cluster or division proportions by condition), with per-sample averaging and faceting.
  • Figure export. Strategy and Illustration tabs render publication-style multi-panel grids. Illustration includes biplot and histogram grids, stacked ridgelines, and population-by-channel median / mean expression heatmaps with per-channel, per-population or z-score scaling and three colour palettes. SVG export uses gridSVG to produce Adobe Illustrator-friendly grouped vector files (rasterised data, vector axes / gates / labels).

How GateLabR compares with other tools

GateLabR fills a specific gap: an interactive GUI for gating that lives natively on a SingleCellExperiment, so it slots into a Bioconductor pipeline instead of replacing it — while keeping full R access to the same object.

GateLabR FlowJo Cytobank CytoExploreR / flowGate
Interface GUI (Shiny) + full R access GUI (desktop, proprietary) GUI (cloud, proprietary) R, with interactive gating helpers
Data object SingleCellExperiment (Bioconductor-native) .wsp workspace cloud workspace GatingSet (flowWorkspace)
Cost / licence Free, MIT, open source Commercial Commercial Free, open source
Flow / CyTOF Both (auto-detected) Flow-focused Both Flow-focused
Gates persist in the object Yes — in metadata(); reload restores everything Workspace files Cloud workspace GatingSet on disk
Gating-ML 2.0 exchange Yes (AND populations) No Yes Partial
Downstream hand-off Populations → colData for diffcyt / CATALYST / any SCE tool Export gated FCS Export gated FCS GatingSet → downstream

Note the Gating-ML row: of the two dominant GUIs, only Cytobank exchanges ISAC Gating-ML 2.0 — FlowJo does not (it uses its own .wsp format). GateLabR reads and writes Cytobank-compatible Gating-ML, so supported gate geometry and AND population trees can move between GateLabR and Cytobank. A population that excludes a gate is written with the schema's gating:use-as-complement in the Cytobank format and through a NOT gate in the standard format, and both are read back, as is the gating:complement attribute GateLab wrote before; such a population has not been shown to cross to Cytobank. OR populations are not imported.

FlowJo is a trademark of Becton, Dickinson and Company. GateLabR is an independent project and is not affiliated with or endorsed by BD or FlowJo.

GateLabR suits two workflows. If your data already lives in R as a SingleCellExperiment, it lets you gate without round-tripping to FlowJo or Cytobank and hand populations straight to diffcyt / CATALYST. But it's equally at home as a plain FCS-in → gate → FCS / stats / Gating-ML out tool: import .fcs files, gate interactively in the GUI, and export — no R fluency needed beyond the one line to launch it.

Installation

GateLabR can be used either as an installed R package or straight from a clone.

Option A — install as a package (recommended)

# install.packages("remotes")            # if needed
# Bioconductor dependencies are resolved automatically; if you don't already
# have BiocManager, install it first: install.packages("BiocManager")
remotes::install_github("david-priest/GateLabR")

library(GateLabR)
launchGatingApp()        # or launchGatingApp(my_sce)

Option B — clone and run from source

# 1. git clone https://github.com/david-priest/GateLabR.git
# 2. From an R session, install dependencies once (CRAN + Bioconductor):
source("path/to/GateLabR/install_dependencies.R")
# 3. Source the launcher and run (see Quick start):
source("path/to/GateLabR/launch.R")
launchGatingApp()

Dependencies (installed automatically by Option A, or by install_dependencies.R for Option B):

  • CRAN: shiny, DT, jsonlite, base64enc, uuid, sp, gridSVG, png, ggplot2
  • Bioconductor: SingleCellExperiment, SummarizedExperiment, S4Vectors, flowCore, xml2

R ≥ 4.2 and Bioconductor ≥ 3.16 are recommended.

Quick start

# Installed package:           Or from a clone:
library(GateLabR)              # source("path/to/GateLabR/launch.R")

# Option A: launch and pass an existing SCE
launchGatingApp(my_sce)

# Option B: launch with no SCE — the app scans the global environment
#           and lets you pick from any SingleCellExperiment objects present
launchGatingApp()

The app opens in your default browser. launchGatingApp() is the single entry point and starts the shared React interface. The three-column layout is:

Panel Content
Left SCE samples and workspace / Gating-ML / FCS actions
Centre Interactive plot (tabs: Gating | Strategy | Illustration | Statistics | Panel)
Right Gates list, population tree, bulk-rename controls

Typical workflow

  1. Get your events into the app, either way:

    • Load an existing SingleCellExperiment — e.g. from CATALYST prepData() (CyTOF) or one you've built from flowCore / your own pipeline.
  2. Call launchGatingApp(sce), or call launchGatingApp() to use the first SCE already loaded in your global environment.

  3. Draw gates on the central plot; the gate list and population tree update live.

  4. Build populations by referencing one or more gates; multiple references use AND logic.

  5. Export results for downstream work — populations as new colData columns on the SCE (Export Population, e.g. for diffcyt, CATALYST, or any SCE-aware analysis), or gated events back out as .fcs.

  6. Press Save to SCE. Besides the workspace this stores which events every population holds, for every hierarchy, so the whole tree can be read in R:

    gatelabHierarchy(sce)                    # one row per population: parent, depth, path, gates, count
    members <- gatelabPopulations(sce)       # logical matrix, events × populations
    sce$population <- gatelabLeafPopulation(sce)   # each event's deepest population, or "ungated"

    These refuse to read memberships that are behind the workspace (gates changed after the last explicit save) unless allow_stale = TRUE; press Save to SCE again to refresh them. The save also gives every event an id in colData(sce)$gatelab_event_id, and the memberships are read back through it, so they follow the events through a reorder or a subset. Events the saved object did not hold, such as those added by cbind(), have no membership, and reading is refused rather than guessed. A population the tree a file is gated under has no counterpart for is not evaluated for that file: its events are NA in it, not FALSE, in the saved memberships and in an exported colData column alike. The save, the export and each read that returns such an NA warn, naming the file and the population.

  7. Save the SCE (e.g. saveRDS(sce, "gated.rds")) — the workspace is embedded in metadata() and reloaded next time.

Data persistence

GateLabR stores its canonical workspace and host provenance inside the SCE:

metadata(sce)$gatelab_workspace
#> revisioned GateLab workspace JSON (gates, populations, scales and settings)

metadata(sce)$gatelab_workspace$memberships
#> every population's membership as a packed bitset, with the hierarchy tables,
#> written by "Save to SCE"; read with gatelabPopulations() and friends

colData(sce)$gatelab_event_id
#> each event's id, written by "Save to SCE"; it ties the stored memberships to their
#> events. cbind() needs it on both objects: give the other object the column as NA
#> (other$gatelab_event_id <- NA_real_) rather than dropping it from the saved one,
#> and the saved events read again once the combined object is subset back to them.

metadata(sce)$gatelabr_compensation
#> compensation profiles, assay bindings, revisions and provenance

rowData(sce)$gatelabr_label
#> optional Panel-tab display-name overrides

metadata(sce)$gatelab_palettes
#> optional: a named colour vector per colData column, used by "Colour by"

The established metadata(sce)$gating_workspace mirror remains available for compatibility with the previous GateLabR interface. It carries each gate's coordinate space and transforms, and GateLabR reads it only when the canonical record above is absent.

File formats supported

  • Input: SingleCellExperiment assays and Cytobank Gating-ML 2.0 XML.
  • Output: FCS, Gating-ML 2.0 (Cytobank-compatible or standard re-importable), SCE .rds / .qs2 (with embedded workspace and assays), per-population colData columns, CSV statistics, SVG / PDF figures.

Related

figtracer — a plain-text, git-tracked electronic lab notebook (ELN) for reproducible bioinformatics: it threads the figures your R / Python analysis produces into living, provenance-tracked lab notes. GateLabR gates your cells; figtracer keeps the resulting figures and analysis as a reproducible written record. Together they cover the single-cell / cytometry workflow from gating to lab notebook.

Citation

If you use GateLabR in published work, please cite the Zenodo archive:

Priest, D. G. (2026). GateLabR: Interactive manual gating for SingleCellExperiment objects. Version 1.0.0. Zenodo. https://doi.org/10.5281/zenodo.20404387

BibTeX:

@software{priest_gatelabr_2026,
  author    = {Priest, David G.},
  title     = {GateLabR: Interactive manual gating for SingleCellExperiment objects},
  year      = {2026},
  version   = {1.0.0},
  doi       = {10.5281/zenodo.20404387},
  url       = {https://github.com/david-priest/GateLabR},
  publisher = {Zenodo}
}

GitHub's "Cite this repository" button (powered by CITATION.cff) renders formatted citations in several styles.

License

MIT — see LICENSE.

Acknowledgements

GateLabR is developed in the Wing Lab at the Immunology Frontier Research Center (IFReC), Osaka University.

Built on top of the Bioconductor stack (SingleCellExperiment, SummarizedExperiment, flowCore), the Shiny web framework, and D3.js for the interactive plot.

Issues and contributions

If you run into a bug, or there's a feature you'd find useful, please open an issue or get in touch — I'm happy to take a look and would be glad to implement it.

A note on .fcs import: import has been thoroughly tested on files from a BD spectral flow cytometer, but not yet on files from other vendors (Beckman Coulter, Cytek, Sony, Thermo, Miltenyi, …). Channel and instrument detection is designed to be vendor-agnostic, but if a file imports incorrectly — missing markers, wrong channels, or a mis-detected flow/CyTOF mode — please let me know (ideally with the file's channel names) and I'll get it sorted.

A note on compensation: embedded-$SPILLOVER compensation has been validated on conventional fluorescence data from a BD FACSAria III / FACSymphony S6, with compensated values matching flowCore::compensate. It has not yet been exercised on embedded matrices from other vendors' acquisition software; the matrix is read vendor-agnostically (flowCore::spillover), so if compensation looks wrong for a given file please send me its channel names and spillover keyword and I'll take a look.

About

Interactive flow and mass cytometry gating in R/Shiny on SingleCellExperiment objects, with Gating-ML round-trip. Browser version (no R): github.com/david-priest/GateLab

Topics

Resources

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages