In this page
GreyCat FCS Library
@library("fcs", "0.0.0");
Reader for Flow Cytometry Standard (FCS) files. Parses and streams multi-parameter flow cytometry data used in cell analysis experiments.
Quick Start
var reader = FcsReader { path: "experiment.fcs" };
// Read file metadata
var meta = reader.meta();
info("File: ${meta.file_name}, Version: ${meta.version}");
info("Events: ${meta.total_events}, Channels: ${meta.total_channels}");
// Print channel names
for (i, ch in meta.channels!!) {
info("Channel ${i}: ${ch.short_name} (${ch.long_name}), range: ${ch.range}");
}
// Stream events
while (reader.can_read()) {
var event = reader.event(); // Array<float?> — one value per channel
// process event...
}
API Reference
FcsReader
Streaming reader for FCS files. Events are read one at a time to support large files without loading everything into memory.
| Method | Returns | Description |
|---|---|---|
meta() |
FcsMeta |
Read file header metadata (channels, version, event count) |
event() |
Array<float?> |
Read next event — array length equals number of channels |
can_read() |
bool |
Check if more events are available |
Constructor:
var reader = FcsReader { path: "path/to/file.fcs" };
FcsMeta
Metadata extracted from the FCS file header.
| Field | Type | Keyword | Description |
|---|---|---|---|
file_name |
String? |
$FIL |
Original FCS file name |
version |
String? |
(HEADER) | FCS format version (e.g., "3.0", "3.1") |
channels |
Array<FcsChannel>? |
$Pn* |
Channel (parameter) definitions |
total_events |
int? |
$TOT |
Total number of events in the file |
total_channels |
int? |
$PAR |
Total number of channels (parameters) |
begin_time |
String? |
$BTIM |
Experiment start timestamp |
end_time |
String? |
$ETIM |
Experiment end timestamp |
cytometer |
String? |
$CYT |
Cytometer type (e.g., "FACSAria", "Aurora") |
keywords |
Map<String, String>? |
all | Every key-value pair of the TEXT segment, plus the supplemental TEXT segment if present |
FcsChannel
Describes a single measured parameter (e.g., fluorescence intensity, scatter).
| Field | Type | Keyword | Description |
|---|---|---|---|
short_name |
String |
$PnN |
Short identifier (e.g., "FSC-A", "PE-A", "SSC-H") |
long_name |
String |
$PnS |
Descriptive name (e.g., "Forward Scatter", "CD4") |
range |
int? |
$PnR |
Maximum value range for this channel |
gain |
float? |
$PnG |
Amplification gain applied |
scale |
Tuple<float, float>? |
$PnE |
Amplification type as (decades, offset); (0, 0) means linear |
wavelength |
int? |
$PnL |
Excitation wavelength in nm; the first one if several are listed |
Typed fields and keywords: why both, and how they overlap
An FCS header is a flat list of key-value pairs (the TEXT segment). The reader exposes it twice:
- Typed fields (the tables above) cover the common standard keywords, parsed into ready-to-use values (
int,float,Tuple, channel objects). keywordsis the complete, raw source: every pair as stored in the file. It exists for everything the typed fields do not cover — the spillover matrix ($SPILLOVER, BD’sSPILL,$COMP), and any standard or vendor-specific keyword (CYTEK_*,BD$*,FJ_*, …) — without needing a new library release for each one.
The overlap is intentional: every keyword with a typed field is also in keywords, as its unparsed string. For example meta.total_events is 65016 and meta.keywords.get("$TOT") is "65016"; channels[1].wavelength is 405 while keywords.get("$P2L") is the full "405,488". Use the typed field when it exists, and keywords for anything else or when you need the exact stored value. If the two ever disagree (e.g. a value containing an escaped delimiter), keywords is the faithful one.
keywords rules:
- Standard (
$-prefixed) and vendor keywords alike, from the TEXT segment and the FCS 3.x supplemental TEXT segment ($BEGINSTEXT/$ENDSTEXT). - Keys keep their original case. The standard treats keywords case-insensitively, so normalise on your side if needed.
- Values are verbatim: not trimmed or converted. The only transformation is the FCS delimiter rule, a doubled delimiter reads as one literal delimiter.
- When a key appears more than once, the last value wins.
- A keyword absent from the file is absent from the map; it is not an error.
var spill = meta.keywords?.get("$SPILLOVER") ?? meta.keywords?.get("SPILL");
Example — Loading Events into a Tensor
var reader = FcsReader { path: "sample.fcs" };
var meta = reader.meta();
var data = Tensor {};
data.init(TensorType::f64, Array<int> { 0, meta.total_channels!! });
while (reader.can_read()) {
var event = reader.event();
var row = Array<float> {};
for (i, v in event) {
row.add(v ?? 0.0);
}
data.append(row);
}
info("Loaded ${meta.total_events} events with ${meta.total_channels} channels");
About FCS Format
FCS (Flow Cytometry Standard) is the standard file format for flow cytometry data, maintained by the International Society for Advancement of Cytometry (ISAC). Each file contains:
- Header — file version and byte offsets
- TEXT segment — key-value metadata (channels, experiment info)
- DATA segment — event measurements (one row per cell/particle, one column per channel)
Common channels include forward scatter (FSC), side scatter (SSC), and various fluorescence parameters for detecting labeled antibodies or dyes.