Gate a SingleCellExperiment or a GatingSet by hand, from your R session.
Open the GateLab app on the object, draw gates on its samples, and read the populations back in R. The gating is stored in the object.
Documentation · Installation · GateLab in your browser
GateLabR opens the GateLab gating application on a SingleCellExperiment or a flowWorkspace GatingSet from an R session, for flow cytometry and mass cytometry (CyTOF) data. It does three things.
The object is gated where it is. launchGatingApp(sce) or launchGatingApp(gs) opens a browser tab on its samples, one at a time or pooled, drawing the assay chosen in the header. Rectangle, polygon, ellipse and quadrant gates build one population tree for the object. The interface is GateLab's, with its Strategy, Illustration, Layout, Plotting and Statistics tabs, and with shiny 1.14 or later the R prompt stays free while the tab is open.
The gating is stored in the object. Gates, populations, scales, compensation records and figure settings are written to metadata(sce), or to a keyword on every sample of a GatingSet, as the work is done and restored when the object is opened again, so saving the object saves the gating. A GatingSet also brings its own hierarchy: the gates it holds open in the app, each in the transformed space the object evaluates it in, with the same counts.
Populations come back to R as data. "Save to SCE" stores which events each population holds, read with gatelabPopulations(), gatelabHierarchy() and gatelabLeafPopulation(). "Export populations to colData" writes a column per population for diffcyt, CATALYST or any other tool that reads the object. On a GatingSet, "Save to GatingSet" writes the populations to its hierarchy as flowWorkspace gates, so gs_get_pop_paths(), gh_pop_get_count(), gs_pop_get_data() and ggcyto read them as they read any node, and save_gs() keeps them. gatelabGatingSet(sce) builds a GatingSet from a gated SCE, its populations written as the same nodes, for flowWorkspace, openCyto, ggcyto and CytoML.
It exists because a Bioconductor cytometry analysis holds its events in a SingleCellExperiment, while manual gating is usually done in a separate program on FCS files, and a gate drawn there has to be exported and matched back to the object's events. GateLabR gates the object itself, so a gated population is a column of the data the next step already uses.
remotes::install_github("david-priest/GateLabR")
library(GateLabR)
launchGatingApp(sce)GateLabR starts from a SingleCellExperiment or a GatingSet and does not read FCS files itself; flowWorkspace::GatingSet(read.flowSet(...)) or CATALYST's prepData() assembles them (the vignette shows both). To gate FCS files without R, use GateLab in the browser, which is the same application.
For flowWorkspace and openCyto users, nothing changes in the object's conventions: a GatingSet parsed from FlowJo, Cytobank or Diva with CytoML, or gated by openCyto, opens with its hierarchy, transforms and compensation as they are; a gate drawn in the app returns as a rectangleGate, polygonGate or ellipsoidGate under its parent's node, named as the population; a population the object's gate types cannot hold (a quadrant, a gate excluded from a population, a polygon drawn in another transform space) returns as its exact per-sample membership, a booleanFilter-class node that gh_pop_get_indices() reads like any other. The object is modified in place, as flowWorkspace always modifies it: a matrix applied in the app goes through compensate() on a writable, uncompensated, untransformed object, and a GatingSet loaded with load_gs() is read-only on its data, so for one of those compensate and transform before launching.
- Draw and edit gates interactively. Polygon, rectangle, ellipse and quadrant gates on any pair of channels, with click-and-drag vertex editing, undo / redo, and per-gate colour and label. A gate can be fitted to the events it holds from its right-click menu.
- 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 fromgating: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 workspaces are read separately (next item). - FlowJo workspace import. The gating strategy of a FlowJo workspace (
.wsp) can be imported onto the object's samples from the Import menu, through the same core as GateLab. After an import, a report sets the event count FlowJo recorded for each population beside GateLabR's own, and says whether a difference arises at a population's own gate or is inherited from one above it. - Agent access. An AI agent (Claude Code, or anything that speaks MCP) can read the gating in the open session and propose gates, which appear in the tab at once as ordinary gates with a badge saying who made them and why.
launchGatingApp(sce, agent = TRUE)opens the tab connected to the agent's relay; setting the relay up is described in GateLab's README. Saving stays yours. - 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
colDatacolumn and overlay multiple samples with distinct colours. - Colour by a
colDatacolumn. 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. Setmetadata(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
colDatacomposition (e.g. cluster or division proportions by condition), with per-sample averaging and faceting. - Figure export. Strategy, Illustration and Layout tabs render publication-style panels. Strategy traces the gating path to one population or to several, with an arrow from each gate to the plot of the population it makes. 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. Layout places plots and text on a page. Figures export as SVG, PDF or PNG.
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 (the GateLab app in a browser tab, served from R) + full R access | GUI (desktop, proprietary) | GUI (cloud, proprietary) | R, with interactive gating helpers |
| Data object | SingleCellExperiment (Bioconductor-native) or GatingSet (flowWorkspace) |
.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(), or in the GatingSet's hierarchy and keywords; reload restores everything |
Workspace files | Cloud workspace | GatingSet on disk |
| Gate exchange | Gating-ML 2.0 import and export (AND populations); FlowJo .wsp import |
.wsp / .wspt workspace formats |
Gating-ML 2.0 import and export | flowWorkspace / CytoML ecosystem |
| Access for AI agents | An MCP server: an agent reads the gating and proposes gates in the open session | No MCP server documented | No MCP server documented | Scripted in R |
| Downstream hand-off | Populations → colData for diffcyt / CATALYST / any SCE tool, or → GatingSet nodes for flowWorkspace / openCyto / ggcyto |
Export gated FCS | Export gated FCS | GatingSet → downstream |
On gate exchange: GateLabR reads and writes Cytobank-compatible Gating-ML, so supported gate geometry and AND population trees can move between GateLabR and Cytobank, and it reads the gating strategy of a FlowJo workspace (.wsp). 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.
On agents: an agent reaches the open tab through a relay on your computer, an MCP server kept in GateLab's tools/agent-mcp, and launchGatingApp(sce, agent = TRUE) opens the tab connected to it. Its gates go through the same session as yours, and nothing is saved to the object until you save. What the agent reads (counts, histograms, medians, pictures of the plot) goes to the agent's model provider; the object stays in your R session. In October 2026 we found no MCP server documented for FlowJo or Cytobank.
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 can be used either as an installed R package or straight from a clone.
# 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)# 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.
# 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. With shiny 1.14 or later the call
returns at once and the prompt is free while the tab is open (the app is
serviced while R is idle); gatelabStop() stops it. A change to the object at
the console while the app runs is kept: the app refuses its next save until
gatelabSync("my_sce") hands it the console's object. With an older shiny the
call blocks until the app is stopped. The three-column layout is:
| Panel | Content |
|---|---|
| Left | SCE samples and workspace / Gating-ML / FCS actions |
| Centre | Interactive plot (tabs: Gating | Strategy | Illustration | Layout | Plotting | Division | Statistics | Metadata | Panel | Scales | Compensation) |
| Right | Gates list, population tree, bulk-rename controls |
-
Get your events into the app, either way:
- Load an existing
SingleCellExperiment— e.g. from CATALYSTprepData()(CyTOF) or one you've built fromflowCore/ your own pipeline.
- Load an existing
-
Call
launchGatingApp(sce), or calllaunchGatingApp()to use the first SCE already loaded in your global environment. -
Draw gates on the central plot; the gate list and population tree update live.
-
Build populations by referencing one or more gates; multiple references use AND logic.
-
Export results for downstream work — populations as new
colDatacolumns on the SCE (Export Population, e.g. fordiffcyt,CATALYST, or any SCE-aware analysis), or gated events back out as.fcs. -
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; pressSave to SCEagain to refresh them. The save also gives every event an id incolData(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 bycbind(), 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 areNAin it, notFALSE, in the saved memberships and in an exportedcolDatacolumn alike. The save, the export and each read that returns such anNAwarn, naming the file and the population. -
Save the SCE (e.g.
saveRDS(sce, "gated.rds")) — the workspace is embedded inmetadata()and reloaded next time.
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.
- Input:
SingleCellExperimentassays, Cytobank Gating-ML 2.0 XML and FlowJo workspaces (.wsp). - 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 / PNG figures.
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.
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.
MIT — see LICENSE.md.
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.
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 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.
