Assays
The plate designer lays out microplates of 6 to 1536 wells for screening, assay development and contract research. Mark wells as samples, standards, positive and negative controls and blanks; give them compounds, concentrations and replicates; make dilution series across or down, number samples, leave the edge wells out and randomise a layout with a seed you can repeat. Every change can be undone, and everything works from the keyboard.
Import a plate reader's export - CSV, TSV or semicolon files, as grids like the plate or lists of wells - and see each read as a heat map, as read or normalised to the plate's own controls, with the Z'-factor, signal to background and each group's spread. Fit four- or five-parameter dose-response curves to every compound, with its IC50 or EC50 and confidence interval, and take the plates away as JSON, the layout as CSV, a worklist of stock and diluent volumes, the fits as CSV, the chart as SVG or the whole assay as a PDF report.
Try it with your own plates and reader exports in confidence: they are opened by your browser, on your device. Nothing is uploaded, no copy reaches us or anyone else, and nothing is kept once you close this tab.
A kinase screen, from plate to IC50
A made-up kinase inhibition assay from a made-up contract lab, Hallam Bioassay: four compounds as ten-point, three-fold dilution series in duplicate on a 96-well plate, uninhibited and fully inhibited controls down the first column and blanks down the last. Import the reader's luminescence export, judge the plate by its Z'-factor, then fit a curve to each compound and read off its IC50.
// Node 20 or later, from the Gold package: fit last night's plates and print the report.
import { readFileSync, writeFileSync } from 'node:fs';
import { readPlates, readReaderFile, normalised, plateQuality, fitPlate, writePlatePdf } from './dist/node/bmx-plate-designer.mjs';
const { document } = readPlates(JSON.parse(readFileSync('hallam-kinase.json', 'utf8')));
const plate = document.plates[0];
const { reads } = await readReaderFile(readFileSync('reader/plate-1.csv', 'utf8'), { format: plate.format });
const quality = plateQuality(plate, reads[0]);
if (quality.zPrime === null || quality.zPrime < 0.5) console.warn(`Z' ${quality.zPrime}: repeat the plate`);
const values = normalised(plate, reads[0], 'inhibition') ?? {};
for (const f of fitPlate(plate, values, { normalise: 'inhibition', fixTop: 100, fixBottom: 0 })) {
console.log(f.group, f.label, f.fit ? `${f.fit.ec50.toPrecision(3)} ${document.unit}` : 'no fit', f.fit?.notes.join(', ') ?? '');
}
writeFileSync('hallam-kinase.pdf', await writePlatePdf({ ...document, plates: [{ ...plate, reads }] }, { normalise: 'inhibition', fixTop: 100, fixBottom: 0 }));
A 384-well screen, laid out and randomised
The next screen: fourteen made-up compounds from the same lab, ten-point, three-fold series in duplicate on a 384-well plate, with controls down columns 2 and 23. Lay it out, leave the edge wells empty of samples against evaporation, randomise the compounds' positions with a seed - the same seed gives the same plate every time - and take the liquid handler its worklist.