← All posts · Michael Baffour Awuah

Phage titration

Plaque counts to PFU/mL, spots included.

Name your lysates, type the plaques you counted, and read the titer off the table. Full plates or spot assays, one HTML file, nothing uploaded — and an Excel workbook on the way out that a spreadsheet can actually compute on.

One HTML file Runs offline Plates + spots Excel + CSV export
2assay formats — full plate at 30–300, spot at 3–30
0dependencies, CDNs, build steps or network requests
1expression shared with the CFU Calculator and Dilution Designer
2exports: a real Excel workbook, or tidy CSV
Channels

A titration is a table, so this is a table

One screen. Name a lysate, type the plaques, read the titer. The parts worth explaining are the ones that stop a plausible-looking number from being wrong.

CH 01QUICK

One row per lysate

The default is a flat table: name, plaques, dilution, volume, PFU/mL. Press Enter in a plaques box and it adds the next row and focuses it, so a titration series types straight down the column without touching the mouse.

CH 02SPOT

Spot assays are first-class

Switch the format and the countable window moves to 3–30, because a 5–10 µL drop saturates far sooner than a full plate. The volume is per row, so a plate and a spot can sit in the same table.

CH 03CARRY-DOWN

Dilution and volume inherit

New rows copy the previous row's dilution and plating volume, because across a titration those barely change. You set them once and then type only counts.

CH 04REPLICATES

Mean ± SD when you need it

One click switches to the replicates view: several plates per lysate, with mean, sample standard deviation and CV% across the countable ones. A lysate with replicates shows its mean in the quick table with its inputs locked, so the two views can never disagree.

CH 05ZERO

Detection limits, not fake zeros

A plate with no plaques does not mean no phage; it means fewer than one plaque's worth. An all-zero lysate reports < limit from its least-diluted plate — which is exactly the number you need when claiming a host is resistant.

CH 06TNTC

Too numerous, or just excluded

Mark a plate TNTC when the lawn was wiped out, or Excl to drop a contaminated one. Both leave the stats and carry the reason into the export, so the summary reads “counted 2 of 3” rather than silently shrinking n.

CH 07LADDER

Any dilution factor

Ten-fold is the default, not an assumption. Set the factor to 5 and step 3 means a 1:5 series diluted three times — 125-fold. The superscript label next to each row updates so you can see what you actually plated.

CH 08EXCEL

A workbook, not a CSV renamed

The Excel button writes a genuine .xlsx: a raw-counts sheet and a summary sheet, with numbers stored as numbers. SUM and AVERAGE work on the titer column immediately.

CH 09LOCAL

Nothing leaves the browser

No network requests at all. Your session autosaves to local storage, and the file works opened straight off the disk — which matters when the plates are in a room the wifi does not reach.

The arithmetic

One formula, and the two numbers people mix up

There is no clever mathematics here. The value of doing it in software is that the same expression runs on every row, with the units kept straight.

PFU/mL  =  plaques × factorstep  ÷  volume plated (mL)
factor
How much you dilute at each step of the series. Ten for a standard ten-fold ladder, five for a 1:5 ladder.
step
Which rung you actually plated. With a factor of ten, step 7 is the 10−7 tube. Step 0 is neat lysate.
volume plated
Entered in microlitres because that is what you pipette, divided by 1000 internally because the answer is per millilitre. On a spot assay this is the drop — often 5 or 10 µL, not 100.
Worked example — a 10 µL spot
plaques counted   22
dilution plated   10−7 (factor 10, step 7)
volume spotted   10 µL = 0.01 mL

22 × 107  =  2.2 × 108
÷ 0.01 mL  =  2.2 × 1010 PFU/mL

Had you left the volume at 100 µL out of habit, this would read 2.2 × 109 — a tenfold undercount, and still a completely believable titer.
The phage-specific trap

The same 22 plaques, two very different titers

Colony counting is nearly always a full plate with a standard volume. Phage work is not: the spot assay is how most of us screen a host range, and it changes two things at once — the volume you plated, and the window in which a count is trustworthy.

Full plate
volume plated100 µL
countable window30–300
22 plaques reads asbelow window
titer at 10−72.2 × 109

Twenty-two on a full plate is a thin count — the tool flags it so you know the number rests on few events.

Spot assay
volume plated10 µL
countable window3–30
22 plaques reads ascountable
titer at 10−72.2 × 1010

The identical count, in a tenth of the volume, is a perfectly good spot — and a titer ten times higher.

Both columns are correct. That is the point: the count alone does not carry the answer, and neither does the dilution. Getting a phage titer right means holding three numbers together, and the one that gets dropped is almost always the volume — because on a full plate it is the same every time, until the day it is not.

Why this exists

Why a twin, and not a switch

I built the CFU Calculator first, for bacteria. Then I went back to the bench, counted plaques instead of colonies, and reached for it out of habit — and it worked. Of course it worked. A plaque and a colony are both one countable thing on a plate. The arithmetic has no idea which one you are looking at.

So the obvious move was a toggle. One tool, a little switch marked CFU/PFU, and the labels change. I sat with that for about a day, and then I built a separate tool instead, which deserves an explanation because on paper it is the worse engineering decision — two files that could drift apart, when one would do.

The reason is that they are not the same job at the bench, even though they are the same sum. Phage titration has the spot assay, which brings its own countable window and its own plating volume, and which the bacterial version has no business defaulting to. When you open a tool to type in plaque counts, every default should already be a phage default. A toggle means the tool is briefly wrong every time you open it, and you have to remember to correct it — which is precisely the class of small habitual error the tool exists to remove.

A toggle is a question. A separate tool is an answer.

What I did do is refuse to let the arithmetic fork. The function that turns a count into a titer is character-identical across the CFU Calculator, this one, and the titering module inside Dilution Designer. Same expression, same guard clauses, same treatment of zero. If I ever find a bug in it, I want the fix to be the same edit in three places rather than three different judgement calls made months apart. The export schema is shared for the same reason: whatever you point at the resulting spreadsheet does not need to know which assay produced it.

And this tool stops early, deliberately. It gives you a titer, a mean and a spread. It does not do efficiency of plating, it does not compare a permissive host against a restrictive one, and it does not run a statistical test — all of which are real phage questions, and all of which already live in Dilution Designer's Titering & EOP module, where an experiment can hold host conditions and a switchable reference. This is the thing you open with a stack of plates and five minutes. That one is the thing you open when the experiment is the point.

As with the others: the science and the design calls are mine, the engineering I did alongside Claude. I am a scientist who has gotten good at building tools with AI, not a career software engineer, and saying so plainly costs me nothing. It also means the honest failure modes get written down — the CFU write-up carries a post-mortem on a unit-display bug that turned microlitres into millilitres in every table I had built, which is the kind of thing you only catch by looking at what actually rendered.

Signal chain

From a stack of plates to a workbook

Nothing to install and no account. Open the page and start typing.

Scope

What it deliberately doesn't do

A small tool stays useful by staying small. These are the edges, so you know before you open it.

No EOP

No efficiency of plating, no reference host, no ratio between conditions. That is the Titering & EOP module in Dilution Designer.

No significance testing

Mean, SD and CV% only — no Welch's t, no log10 modelling. A bench calculator is the wrong place to pick a statistical test.

No plots

No charts and no figures. Dilution Designer draws publication-styled titer charts; for colony-count figures there is CFU Plot Studio.

One session, one browser

Data lives in one browser on one machine, which is the price of never uploading anything. Export if you want it elsewhere.

Free · offline · one file

Count the plaques. Let something else do the dividing.

Free, open source, and it runs entirely in your own browser. Nothing to install, nothing uploaded.