8.4.614-stable Switch to dev

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).
  • keywords is 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’s SPILL, $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.