Skip to content

ebalderasr/flowjo-sweetspot-web

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FlowJo SweetSpot Web

Functional flow cytometry panel optimization for CHO cells


→ Open the live app


Stack Focus License Part of


What is FlowJo SweetSpot Web?

FlowJo SweetSpot Web is a browser-based panel optimization tool for functional flow cytometry in CHO cell cultures. Upload a FlowJo single-stain titration export and it identifies the optimal concentration for each dye — the one that maximizes the fraction of cells detected in the target channel while minimizing spectral contamination of the other channels.

It runs entirely in the browser through vanilla JavaScript. No installation, no server, no data leaves your machine.


The problem: dyes are not antibodies

How Stain Index was designed to work

The Stain Index (SI) is the standard metric for flow cytometry panel optimization:

$$SI = \frac{MFI_{\text{bright}} - MFI_{\text{dim}}}{2 \times rSD_{\text{dim}}}$$

It measures how well two populations are resolved: the bright (positive) cells and the dim (negative) cells. The denominator anchors the measurement to the spread of the negative cluster, so a high SI means the two populations are cleanly separated and the instrument can reliably assign each cell to one group.

This works because antibody-conjugated fluorophores create genuine bimodality. A CD3-PE antibody stains T cells and leaves all other cells unstained. Biology itself creates the two populations. The job of panel optimization is then to choose reagent concentrations that maximize the gap between those clusters — and SI captures that gap directly.

Why functional dyes break this framework

Physiological dyes — TMRM (mitochondrial potential), Bodipy (lipid content), CellRox Deep Red (oxidative stress), and GFP as a recombinant reporter — do not produce two populations. They load into every cell based on that cell's current metabolic or physiological state. The result is a single continuous distribution that shifts along the fluorescence axis as a unit. There is no biologically negative subpopulation.

This means the classic SI optimization question — how do I maximize the gap between positive and negative? — does not apply. With dyes, all cells are positive. What varies is how positive each cell is, and that variation is the biological information you are trying to preserve.

The real optimization problem for dye panels

Because dye uptake is concentration-dependent, the experimenter controls the brightness of the distribution by choosing the staining concentration. Higher concentration → brighter signal → apparently higher SI. But this creates a false incentive:

Problem What happens at high concentration Consequence
Detector saturation The upper tail of the distribution hits the instrument ceiling Cells that differ biologically are compressed to the same voltage; heterogeneity is lost
Spreading error Brighter signals produce more photon shot noise in adjacent channels Other dyes in the panel are harder to resolve
Hidden heterogeneity The distribution compresses near the detector limit Cell-to-cell differences in metabolic state — the biological signal of interest — become invisible
Multivariate corruption Spreading error from one dye invades other channels Mitochondrial potential, lipid content, and ROS can no longer be analyzed jointly as independent measurements

This tool's approach

Since there is no biological negative population, this tool uses the unstained control as a surrogate dim reference — the cells with zero dye present, whose fluorescence reflects only autofluorescence and instrument noise. This lets SI remain a meaningful indicator of whether the signal clears the noise floor.

But SI alone still does not answer the real question. The primary decision criterion is therefore population behavior: at a given concentration, what fraction of cells is being captured in the target channel, and what fraction is spilling into channels that belong to other dyes? The concentration that maximizes the first while minimizing the second — above a minimum signal quality threshold — is the sweet spot.

This is what the Score metric computes directly, without assuming that brighter is always better.


How it works

1. Prepare the FlowJo export

In FlowJo, set up the following gate structure under your parent gate (e.g. CHO/Singlets):

CHO/Singlets
├── GFP-A+       GFP-A-
├── PE-A+        PE-A-
├── SNARF-A+     SNARF-A-
└── APC-A+       APC-A-

In the Table Editor, export for each sample the following statistics per gate:

  • Freq. of Parent
  • Median ({channel})
  • Robust SD ({channel})

Add three metadata columns: Sample, Colorante (dye name), and Concentracion (concentration). Include one row per stained sample and one row for the unstained control with Colorante = ST.

Export as .csv or .xlsx. One sheet, one row per sample.

The app recognizes English aliases automatically: Dye, Stain, Marker for the dye column; Concentration, Dose for the concentration column.

2. Configure

Parameter Description Default
Population base Parent gate path as it appears in column names CHO/Singlets
Detector max Upper instrument limit for saturation estimation 1 000 000
Minimum acceptable SI SI floor below which a concentration is penalized 7
Target channel Detector channel for each dye editable per dye

GFP is marked informational_only: its expression level is set by the biology of the cell, not by a titrable dose. It participates in invasion calculations — its spill can reach other channels, and other dyes can spill into its channel — but no concentration selection is performed for it.

3. Run the analysis

Click Run analysis. For each stained sample (dye × concentration × clone), the engine computes:

  • Stain Index against the unstained control
  • Fraction of cells in the target channel's positive gate
  • Fraction of cells falling in each neighbor channel's positive gate (invasion)
  • Score, saturation envelope, and selection outcome

4. Read the results

Cards at the top show the recommended concentration for each dye with a one-line interpretation.

Metric trends plot (3 panels, top to bottom):

  1. Stain Index — reference signal quality against the unstained control
  2. Invasion total — fraction of cells appearing in non-target positive gates; lower is better
  3. Score — the combined metric; the peak of this curve is the recommended concentration

Selection criteria plot — one panel per dye showing the Score curve across concentrations, with the selected condition marked.

Best conditions table — final recommendation with Score, % positivas, invasion total, SI, and saturation flags per dye.

Ws breakdown table — per-neighbor invasion detail: frequency of cells invading each neighbor channel and the apparent SI in that channel (diagnostic).


Methods

Stain Index

$$SI = \frac{MFI_{\text{stained+}} - MFI_{\text{ST-}}}{2 \times rSD_{\text{ST-}}}$$

Where $MFI_{\text{stained+}}$ is the median of the stained sample's positive gate, and $MFI_{\text{ST-}}$ and $rSD_{\text{ST-}}$ are the median and robust standard deviation of the unstained control's negative gate. The unstained control provides the noise floor reference for all dyes in the panel simultaneously.

SI is used as a correction factor, not as the primary decision driver. See SI factor below.

Saturation envelope

$$UpperEnvelope = MFI_{\text{stained+}} + k \times rSD_{\text{stained+}} \quad (k = 2)$$

A concentration is flagged as Clip_Risk when $UpperEnvelope \geq detector_max$, and as Near_Clip when $UpperEnvelope \geq 0.9 \times detector_max$. Clip-risk concentrations are excluded from the candidate pool when alternatives exist.

Freq_Signal

$$Freq_Signal = \frac{\text{cells in target+ gate}}{\text{parent cells}} \in [0, 1]$$

The primary signal metric: what fraction of the population is being captured in the target channel at this concentration? More is better — a higher fraction means more of the biological heterogeneity is represented in the measurement.

Invasion_Total

For each neighbor channel $j \neq$ target:

$$FreqInvasion_j = \frac{\text{cells in channel } j \text{ positive gate (stained sample)}}{\text{parent cells}}$$

$$Invasion_Total = \sum_{j \neq \text{target}} FreqInvasion_j$$

This measures how much of the population is being falsely detected in the wrong channels due to spectral spillover at this concentration. Lower is better.

Apparent SI (diagnostic only): for each neighbor $j$, the app also computes how strongly the invading signal appears in that channel: $$ApparentSI_j = \frac{MFI_{j,,\text{stained+}} - MFI_{j,,\text{ST-}}}{2 \times rSD_{j,,\text{ST-}}}$$ This is stored in the Ws breakdown table and useful for identifying which neighbors are most affected, but it does not enter the Score calculation.

SI factor

$$SI_factor = \min!\left(\max!\left(\frac{SI}{SI_{\min}},, 0\right),, 1\right)$$

A soft gate on signal quality. It ramps linearly from 0 to 1 as SI goes from 0 to $SI_{\min}$ (the minimum acceptable SI configured by the user), then stays fixed at 1 for any concentration above the threshold. This means:

  • A concentration with insufficient signal is penalized proportionally — it cannot win if better options exist
  • A concentration that exceeds the threshold is not further rewarded — extra brightness beyond the minimum does not improve the score

Score

$$\boxed{Score = \frac{Freq_Signal}{1 + Invasion_Total} \times SI_factor}$$

The score captures the three-way balance the experiment actually requires:

Component Behavior
Freq_Signal ↑ More cells detected → Score rises
Invasion_Total ↑ More cells in wrong channels → Score falls
SI_factor < 1 Insufficient signal → Score reduced proportionally
SI_factor = 1 Signal is adequate → frequencies alone drive the decision

The optimal concentration is the one that places the most cells in the right channel, while spilling the fewest into others, given that the signal clears the minimum quality threshold.

Selection algorithm

Given all concentrations for a dye:

  1. Safety filter: remove concentrations with Clip_Risk = True if any safe alternatives exist; then prefer non-Near_Clip concentrations
  2. SI floor: if any candidate achieves $SI \geq SI_{\min}$, restrict the candidate pool to those; otherwise retain all (status BELOW_SI_THRESHOLD)
  3. Data quality: prefer candidates with fewer neighbor channels with missing invasion data
  4. Select: $\arg\max(Score)$ within the candidate pool

Features

Zero installation Runs fully client-side — no Python, no pip, no server
Frequency-first scoring Decision is based on population behavior, not signal intensity alone
SI as correction factor Stain Index gates insufficient signal without over-rewarding brightness
Saturation detection Upper envelope flags concentrations near or at the detector ceiling
GFP-aware Recombinant reporter participates in invasion estimates but is not titrated
Clone support Multiple CHO clones in the same export are tracked separately
Interactive plots Plotly-powered; zoom, hover, and compare conditions
CSV / XLSX input Accepts both formats; auto-detects percentage vs. fraction frequency scale
Downloadable results Best conditions and full results exported as CSV in one click
No data upload Everything runs locally in the browser — no data leaves your machine

Input format

FlowJo export columns

The app expects one row per stained sample. Column names are constructed from the Population base and channel settings.

Metadata columns (required)

Column Aliases Description
Sample Sample or file name
Colorante Dye, Stain, Marker Dye name (see canonical names below)
Concentracion Concentration, Dose Concentration or identifier

Gate statistics per channel (required for each dye in the panel)

Using CHO/Singlets as population base and PE-A as an example channel:

Column Description
CHO/Singlets/PE-A+ | Freq. of Parent Fraction of cells in the positive gate
CHO/Singlets/PE-A+ | Median (PE-A) Median fluorescence of the positive gate
CHO/Singlets/PE-A+ | Robust SD (PE-A) Robust SD of the positive gate
CHO/Singlets/PE-A- | Median (PE-A) Median fluorescence of the negative gate
CHO/Singlets/PE-A- | Robust SD (PE-A) Robust SD of the negative gate

Canonical dye names

The following names are recognized (case-insensitive):

Input name Canonical name Default channel
ST, unstained, Control ST unstained Unstained control
GFP GFP (proteina recombinante) GFP-A
TMRM TMRM PE-A
Bodipy Bodipy SNARF-A
CellRox, CellRox Deep Red CellRox Deep Red APC-A

Channels can be reassigned in the Dye configuration table without editing the file.


Tech stack

Analysis and visualization

Plotly XLSX

Frontend

HTML5 CSS3 JavaScript


Project structure

flowjo-sweetspot-web/
├── index.html      ← markup and help panels
├── styles.css      ← all custom styles
└── app.js          ← analysis engine, UI logic, and plots

Author

Emiliano Balderas Ramírez Bioengineer · PhD Candidate in Biochemical Sciences Instituto de Biotecnología (IBt), UNAM

LinkedIn Email


Related

Clonalyzer 2 — Fed-batch kinetics analysis for CHO cultures: specific rates, yields, and integral cell counts from a single CSV drop.

CellSplit — Neubauer cell counting and passage planning for CHO cultures.

CellBlock — Shared biosafety cabinet scheduling for cell culture research groups.


FlowJo SweetSpot Web — drop a titration export, find the sweet spot.

Releases

No releases published

Packages

 
 
 

Contributors